@@ -27,11 +27,14 @@
Loading
27 27
28 28
namespace Libplanet.Tests.Blockchain
29 29
{
30 -
    public class BlockChainTest : IDisposable
30 +
    public partial class BlockChainTest : IDisposable
31 31
    {
32 32
        private StoreFixture _fx;
33 +
        private BlockPolicy<DumbAction> _policy;
33 34
        private BlockChain<DumbAction> _blockChain;
34 35
        private RecordingRenderer<DumbAction> _renderer;
36 +
        private Block<DumbAction> _validNext;
37 +
        private List<Transaction<DumbAction>> _emptyTransaction;
35 38
36 39
        public BlockChainTest(ITestOutputHelper output)
37 40
        {
@@ -44,14 +47,25 @@
Loading
44 47
45 48
            _fx = new DefaultStoreFixture(memory: true);
46 49
            _renderer = new RecordingRenderer<DumbAction>();
50 +
            _policy = new BlockPolicy<DumbAction>(new MinerReward(1));
47 51
            _blockChain = new BlockChain<DumbAction>(
48 -
                new BlockPolicy<DumbAction>(new MinerReward(1)),
52 +
                _policy,
49 53
                _fx.Store,
50 54
                _fx.StateStore,
51 55
                _fx.GenesisBlock,
52 56
                renderers: new[] { new LoggedActionRenderer<DumbAction>(_renderer, Log.Logger) }
53 57
            );
54 58
            _renderer.ResetRecords();
59 +
60 +
            _emptyTransaction = new List<Transaction<DumbAction>>();
61 +
            _validNext = Block<DumbAction>.Mine(
62 +
                1,
63 +
                1024,
64 +
                _fx.GenesisBlock.TotalDifficulty,
65 +
                _fx.GenesisBlock.Miner.Value,
66 +
                _fx.GenesisBlock.Hash,
67 +
                _fx.GenesisBlock.Timestamp.AddSeconds(1),
68 +
                _emptyTransaction);
55 69
        }
56 70
57 71
        public void Dispose()
@@ -83,12 +97,7 @@
Loading
83 97
        [Fact]
84 98
        public async void MineBlockWithPendingTxs()
85 99
        {
86 -
            var keys = new[]
87 -
            {
88 -
                new PrivateKey(),
89 -
                new PrivateKey(),
90 -
                new PrivateKey(),
91 -
            };
100 +
            var keys = new[] { new PrivateKey(), new PrivateKey(), new PrivateKey() };
92 101
93 102
            var txs = new[]
94 103
            {
@@ -282,11 +291,7 @@
Loading
282 291
        [Fact]
283 292
        public void UnstageTransaction()
284 293
        {
285 -
            Transaction<DumbAction>[] txs = new[]
286 -
            {
287 -
                _fx.Transaction1,
288 -
                _fx.Transaction2,
289 -
            };
294 +
            Transaction<DumbAction>[] txs = { _fx.Transaction1, _fx.Transaction2 };
290 295
            Assert.Empty(_blockChain.GetStagedTransactionIds());
291 296
292 297
            StageTransactions(txs);
@@ -2473,7 +2478,6 @@
Loading
2473 2478
            var storeFixture = new DefaultStoreFixture();
2474 2479
            var policy = new NullPolicy<DumbAction>();
2475 2480
2476 -
            var timestamp = new DateTimeOffset(2019, 11, 20, 0, 0, 0, TimeSpan.Zero);
2477 2481
            var addresses = ImmutableList<Address>.Empty
2478 2482
                .Add(storeFixture.Address1)
2479 2483
                .Add(storeFixture.Address2)
@@ -2547,8 +2551,7 @@
Loading
2547 2551
            var store = new DefaultStore(null);
2548 2552
            var stateStore = new TrieStateStore(
2549 2553
                new MemoryKeyValueStore(), new MemoryKeyValueStore());
2550 -
            var genesisBlock =
2551 -
                TestUtils.MineGenesis<DumbAction>(
2554 +
            var genesisBlock = TestUtils.MineGenesis<DumbAction>(
2552 2555
                    blockAction: _blockChain.Policy.BlockAction, checkStateRootHash: true);
2553 2556
            BlockChain<DumbAction> blockChain = TestUtils.MakeBlockChain(
2554 2557
                _blockChain.Policy, store, stateStore: stateStore, genesisBlock: genesisBlock);
@@ -2574,11 +2577,9 @@
Loading
2574 2577
            {
2575 2578
            }
2576 2579
2577 -
            public IValue PlainValue =>
2578 -
                default(Dictionary);
2580 +
            public IValue PlainValue => default(Dictionary);
2579 2581
2580 -
            public void LoadPlainValue(
2581 -
                IValue plainValue)
2582 +
            public void LoadPlainValue(IValue plainValue)
2582 2583
            {
2583 2584
            }
2584 2585

@@ -3,6 +3,7 @@
Loading
3 3
using System.Collections.Immutable;
4 4
using System.Diagnostics.CodeAnalysis;
5 5
using System.Linq;
6 +
using System.Numerics;
6 7
using System.Security.Cryptography;
7 8
using System.Threading;
8 9
using System.Threading.Tasks;
@@ -860,8 +861,7 @@
Loading
860 861
            Block<T> prevTip = Tip;
861 862
            try
862 863
            {
863 -
                InvalidBlockException e =
864 -
                    Policy.ValidateNextBlock(this, block);
864 +
                InvalidBlockException e = ValidateNextBlock(block);
865 865
866 866
                if (!(e is null))
867 867
                {
@@ -1664,6 +1664,96 @@
Loading
1664 1664
            }
1665 1665
        }
1666 1666
1667 +
        private InvalidBlockException ValidateNextBlock(Block<T> nextBlock)
1668 +
        {
1669 +
            InvalidBlockException e = Policy.ValidateNextBlock(this, nextBlock);
1670 +
1671 +
            if (!(e is null))
1672 +
            {
1673 +
                return e;
1674 +
            }
1675 +
1676 +
            long index = this.Count;
1677 +
            long difficulty = Policy.GetNextBlockDifficulty(this);
1678 +
            BigInteger totalDifficulty = index >= 1
1679 +
                    ? this[index - 1].TotalDifficulty + nextBlock.Difficulty
1680 +
                    : nextBlock.Difficulty;
1681 +
1682 +
            Block<T> lastBlock = index >= 1 ? this[index - 1] : null;
1683 +
            HashDigest<SHA256>? prevHash = lastBlock?.Hash;
1684 +
            DateTimeOffset? prevTimestamp = lastBlock?.Timestamp;
1685 +
1686 +
            if (nextBlock.Index != index)
1687 +
            {
1688 +
                return new InvalidBlockIndexException(
1689 +
                    $"The expected block index is #{index}, but its index " +
1690 +
                    $"is #{nextBlock.Index}.");
1691 +
            }
1692 +
1693 +
            if (nextBlock.Difficulty < difficulty)
1694 +
            {
1695 +
                return new InvalidBlockDifficultyException(
1696 +
                    $"The expected difficulty of the block #{index} " +
1697 +
                    $"is {difficulty}, but its difficulty is " +
1698 +
                    $"{nextBlock.Difficulty}.");
1699 +
            }
1700 +
1701 +
            if (nextBlock.TotalDifficulty != totalDifficulty)
1702 +
            {
1703 +
                var msg = $"The expected total difficulty of the block #{index} " +
1704 +
                          $"is {totalDifficulty}, but its difficulty is " +
1705 +
                          $"{nextBlock.TotalDifficulty}.";
1706 +
                return new InvalidBlockTotalDifficultyException(
1707 +
                    nextBlock.Difficulty,
1708 +
                    nextBlock.TotalDifficulty,
1709 +
                    msg);
1710 +
            }
1711 +
1712 +
            if (!nextBlock.PreviousHash.Equals(prevHash))
1713 +
            {
1714 +
                if (prevHash is null)
1715 +
                {
1716 +
                    return new InvalidBlockPreviousHashException(
1717 +
                        "the genesis block must have not previous block");
1718 +
                }
1719 +
1720 +
                return new InvalidBlockPreviousHashException(
1721 +
                    $"The block #{index} is not continuous from the " +
1722 +
                    $"block #{index - 1}; while previous block's hash is " +
1723 +
                    $"{prevHash}, the block #{index}'s pointer to " +
1724 +
                    "the previous hash refers to " +
1725 +
                    (nextBlock.PreviousHash?.ToString() ?? "nothing") + ".");
1726 +
            }
1727 +
1728 +
            if (nextBlock.Timestamp < prevTimestamp)
1729 +
            {
1730 +
                return new InvalidBlockTimestampException(
1731 +
                    $"The block #{index}'s timestamp " +
1732 +
                    $"({nextBlock.Timestamp}) is earlier than" +
1733 +
                    $" the block #{index - 1}'s ({prevTimestamp}).");
1734 +
            }
1735 +
1736 +
            if (StateStore is TrieStateStore trieStateStore)
1737 +
            {
1738 +
                ExecuteActions(nextBlock, StateCompleterSet<T>.Recalculate);
1739 +
                HashDigest<SHA256> rootHash =
1740 +
                    trieStateStore.GetRootHash(nextBlock.Hash);
1741 +
1742 +
                if (!rootHash.Equals(nextBlock.StateRootHash))
1743 +
                {
1744 +
                    var message = $"The block #{index}'s state root hash is " +
1745 +
                                  $"{nextBlock.StateRootHash?.ToString()}, but the execution " +
1746 +
                                  $"result is {rootHash.ToString()}.";
1747 +
                    return new InvalidBlockStateRootHashException(
1748 +
                        nextBlock.StateRootHash,
1749 +
                        rootHash,
1750 +
                        message);
1751 +
                }
1752 +
            }
1753 +
1754 +
            return null;
1755 +
        }
1756 +
1667 1757
        private IValue GetRawState(
1668 1758
            string key,
1669 1759
            HashDigest<SHA256>? offset,

@@ -1,16 +1,11 @@
Loading
1 1
using System;
2 -
using System.Collections.Generic;
3 -
using System.Security.Cryptography;
4 2
using System.Threading.Tasks;
5 3
using Libplanet.Blockchain;
6 4
using Libplanet.Blockchain.Policies;
7 -
using Libplanet.Blocks;
8 5
using Libplanet.Crypto;
9 6
using Libplanet.Store;
10 -
using Libplanet.Store.Trie;
11 7
using Libplanet.Tests.Common.Action;
12 8
using Libplanet.Tests.Store;
13 -
using Libplanet.Tests.Store.Trie;
14 9
using Libplanet.Tx;
15 10
using Xunit;
16 11
using Xunit.Abstractions;
@@ -27,9 +22,6 @@
Loading
27 22
        private StoreFixture _fx;
28 23
        private BlockChain<DumbAction> _chain;
29 24
        private IBlockPolicy<DumbAction> _policy;
30 -
        private List<Transaction<DumbAction>> _emptyTransaction;
31 -
        private Block<DumbAction> _genesis;
32 -
        private Block<DumbAction> _validNext;
33 25
34 26
        public BlockPolicyTest(ITestOutputHelper output)
35 27
        {
@@ -45,16 +37,6 @@
Loading
45 37
                _fx.Store,
46 38
                _fx.StateStore,
47 39
                _fx.GenesisBlock);
48 -
            _emptyTransaction = new List<Transaction<DumbAction>>();
49 -
            _genesis = _chain.Genesis;
50 -
            _validNext = Block<DumbAction>.Mine(
51 -
                1,
52 -
                1024,
53 -
                _genesis.TotalDifficulty,
54 -
                _genesis.Miner.Value,
55 -
                _genesis.Hash,
56 -
                _genesis.Timestamp.AddSeconds(1),
57 -
                _emptyTransaction);
58 40
        }
59 41
60 42
        public void Dispose()
@@ -162,164 +144,5 @@
Loading
162 144
                _policy.GetNextBlockDifficulty(chain)
163 145
            );
164 146
        }
165 -
166 -
        [Fact]
167 -
        public void ValidateNextBlock()
168 -
        {
169 -
            var validNextBlock = Block<DumbAction>.Mine(
170 -
                1,
171 -
                1,
172 -
                _genesis.TotalDifficulty,
173 -
                _genesis.Miner.Value,
174 -
                _genesis.Hash,
175 -
                _genesis.Timestamp.AddDays(1),
176 -
                _emptyTransaction);
177 -
            _policy.ValidateNextBlock(_chain, validNextBlock);
178 -
        }
179 -
180 -
        [Fact]
181 -
        public void ValidateNextBlockInvalidIndex()
182 -
        {
183 -
            _chain.Append(_validNext);
184 -
185 -
            var invalidIndexBlock = Block<DumbAction>.Mine(
186 -
                1,
187 -
                1,
188 -
                _genesis.TotalDifficulty,
189 -
                _genesis.Miner.Value,
190 -
                _validNext.Hash,
191 -
                _validNext.Timestamp.AddSeconds(1),
192 -
                _emptyTransaction);
193 -
            Assert.IsType<InvalidBlockIndexException>(
194 -
                _policy.ValidateNextBlock(_chain, invalidIndexBlock));
195 -
        }
196 -
197 -
        [Fact]
198 -
        public void ValidateNextBlockInvalidDifficulty()
199 -
        {
200 -
            _chain.Append(_validNext);
201 -
202 -
            var invalidDifficultyBlock = Block<DumbAction>.Mine(
203 -
                2,
204 -
                1,
205 -
                _validNext.TotalDifficulty,
206 -
                _genesis.Miner.Value,
207 -
                _validNext.Hash,
208 -
                _validNext.Timestamp.AddSeconds(1),
209 -
                _emptyTransaction);
210 -
            Assert.IsType<InvalidBlockDifficultyException>(
211 -
                _policy.ValidateNextBlock(
212 -
                    _chain,
213 -
                    invalidDifficultyBlock));
214 -
        }
215 -
216 -
        [Fact]
217 -
        public void ValidateNextBlockInvalidTotalDifficulty()
218 -
        {
219 -
            _chain.Append(_validNext);
220 -
221 -
            var invalidTotalDifficultyBlock = Block<DumbAction>.Mine(
222 -
                2,
223 -
                _policy.GetNextBlockDifficulty(_chain),
224 -
                _validNext.TotalDifficulty - 1,
225 -
                _genesis.Miner.Value,
226 -
                _validNext.Hash,
227 -
                _validNext.Timestamp.AddSeconds(1),
228 -
                _emptyTransaction);
229 -
            Assert.IsType<InvalidBlockTotalDifficultyException>(
230 -
                _policy.ValidateNextBlock(
231 -
                    _chain,
232 -
                    invalidTotalDifficultyBlock));
233 -
        }
234 -
235 -
        [Fact]
236 -
        public void ValidateNextBlockInvalidPreviousHash()
237 -
        {
238 -
            _chain.Append(_validNext);
239 -
240 -
            var invalidPreviousHashBlock = Block<DumbAction>.Mine(
241 -
                2,
242 -
                1032,
243 -
                _validNext.TotalDifficulty,
244 -
                _genesis.Miner.Value,
245 -
                new HashDigest<SHA256>(new byte[32]),
246 -
                _validNext.Timestamp.AddSeconds(1),
247 -
                _emptyTransaction);
248 -
            Assert.IsType<InvalidBlockPreviousHashException>(
249 -
                _policy.ValidateNextBlock(
250 -
                    _chain,
251 -
                    invalidPreviousHashBlock));
252 -
        }
253 -
254 -
        [Fact]
255 -
        public void ValidateNextBlockInvalidTimestamp()
256 -
        {
257 -
            _chain.Append(_validNext);
258 -
259 -
            var invalidPreviousTimestamp = Block<DumbAction>.Mine(
260 -
                2,
261 -
                1032,
262 -
                _validNext.TotalDifficulty,
263 -
                _genesis.Miner.Value,
264 -
                _validNext.Hash,
265 -
                _validNext.Timestamp.Subtract(TimeSpan.FromSeconds(1)),
266 -
                _emptyTransaction);
267 -
            Assert.IsType<InvalidBlockTimestampException>(
268 -
                _policy.ValidateNextBlock(
269 -
                    _chain,
270 -
                    invalidPreviousTimestamp));
271 -
        }
272 -
273 -
        [Fact]
274 -
        public void ValidateNextBlockInvalidStateRootHash()
275 -
        {
276 -
            IKeyValueStore stateKeyValueStore = new MemoryKeyValueStore(),
277 -
                stateHashKeyValueStore = new MemoryKeyValueStore();
278 -
            var stateStore = new TrieStateStore(stateKeyValueStore, stateHashKeyValueStore);
279 -
            // FIXME: It assumes that _fx.GenesisBlock doesn't update any states with transactions.
280 -
            //        Actually, it depends on BlockChain<T> to update states and it makes hard to
281 -
            //        calculate state root hash. To resolve this problem,
282 -
            //        it should be moved into StateStore.
283 -
            var genesisBlock = TestUtils.MineGenesis<DumbAction>(
284 -
                blockAction: _policy.BlockAction, checkStateRootHash: true);
285 -
            var store = new DefaultStore(null);
286 -
            var chain = new BlockChain<DumbAction>(
287 -
                _policy,
288 -
                store,
289 -
                stateStore,
290 -
                genesisBlock);
291 -
            var validNext = Block<DumbAction>.Mine(
292 -
                1,
293 -
                1024,
294 -
                genesisBlock.TotalDifficulty,
295 -
                genesisBlock.Miner.Value,
296 -
                genesisBlock.Hash,
297 -
                genesisBlock.Timestamp.AddSeconds(1),
298 -
                _emptyTransaction);
299 -
            chain.ExecuteActions(validNext);
300 -
            validNext =
301 -
                new Block<DumbAction>(validNext, stateStore.GetRootHash(validNext.Hash));
302 -
            chain.Append(validNext);
303 -
304 -
            var invalidStateRootHash = Block<DumbAction>.Mine(
305 -
                2,
306 -
                1032,
307 -
                validNext.TotalDifficulty,
308 -
                genesisBlock.Miner.Value,
309 -
                validNext.Hash,
310 -
                validNext.Timestamp.AddSeconds(1),
311 -
                _emptyTransaction);
312 -
            var actionEvaluations = _chain.BlockEvaluator.EvaluateActions(
313 -
                invalidStateRootHash,
314 -
                StateCompleterSet<DumbAction>.Recalculate);
315 -
            chain.SetStates(invalidStateRootHash, actionEvaluations, false);
316 -
            invalidStateRootHash = new Block<DumbAction>(
317 -
                invalidStateRootHash,
318 -
                new HashDigest<SHA256>(TestUtils.GetRandomBytes(HashDigest<SHA256>.Size)));
319 -
            Assert.IsType<InvalidBlockStateRootHashException>(
320 -
                _policy.ValidateNextBlock(
321 -
                    chain,
322 -
                    invalidStateRootHash));
323 -
        }
324 147
    }
325 148
}

@@ -0,0 +1,172 @@
Loading
1 +
using System;
2 +
using System.Security.Cryptography;
3 +
using Libplanet.Blockchain;
4 +
using Libplanet.Blockchain.Policies;
5 +
using Libplanet.Blocks;
6 +
using Libplanet.Store;
7 +
using Libplanet.Store.Trie;
8 +
using Libplanet.Tests.Common.Action;
9 +
using Libplanet.Tests.Store.Trie;
10 +
using Xunit;
11 +
12 +
namespace Libplanet.Tests.Blockchain
13 +
{
14 +
    public partial class BlockChainTest
15 +
    {
16 +
        [Fact]
17 +
        public void ValidateNextBlock()
18 +
        {
19 +
            var validNextBlock = Block<DumbAction>.Mine(
20 +
                1,
21 +
                1024,
22 +
                _fx.GenesisBlock.TotalDifficulty,
23 +
                _fx.GenesisBlock.Miner.Value,
24 +
                _fx.GenesisBlock.Hash,
25 +
                _fx.GenesisBlock.Timestamp.AddDays(1),
26 +
                _emptyTransaction);
27 +
            _blockChain.Append(validNextBlock);
28 +
            Assert.Equal(_blockChain.Tip, validNextBlock);
29 +
        }
30 +
31 +
        [Fact]
32 +
        private void ValidateNextBlockInvalidIndex()
33 +
        {
34 +
            _blockChain.Append(_validNext);
35 +
36 +
            var invalidIndexBlock = Block<DumbAction>.Mine(
37 +
                1,
38 +
                1,
39 +
                _fx.GenesisBlock.TotalDifficulty,
40 +
                _fx.GenesisBlock.Miner.Value,
41 +
                _validNext.Hash,
42 +
                _validNext.Timestamp.AddSeconds(1),
43 +
                _emptyTransaction);
44 +
            Assert.Throws<InvalidBlockIndexException>(() =>
45 +
                _blockChain.Append(invalidIndexBlock));
46 +
        }
47 +
48 +
        [Fact]
49 +
        private void ValidateNextBlockInvalidDifficulty()
50 +
        {
51 +
            _blockChain.Append(_validNext);
52 +
53 +
            var invalidDifficultyBlock = Block<DumbAction>.Mine(
54 +
                2,
55 +
                1,
56 +
                _validNext.TotalDifficulty,
57 +
                _fx.GenesisBlock.Miner.Value,
58 +
                _validNext.Hash,
59 +
                _validNext.Timestamp.AddSeconds(1),
60 +
                _emptyTransaction);
61 +
            Assert.Throws<InvalidBlockDifficultyException>(() =>
62 +
                    _blockChain.Append(invalidDifficultyBlock));
63 +
        }
64 +
65 +
        [Fact]
66 +
        private void ValidateNextBlockInvalidTotalDifficulty()
67 +
        {
68 +
            _blockChain.Append(_validNext);
69 +
70 +
            var invalidTotalDifficultyBlock = Block<DumbAction>.Mine(
71 +
                2,
72 +
                _policy.GetNextBlockDifficulty(_blockChain),
73 +
                _validNext.TotalDifficulty - 1,
74 +
                _fx.GenesisBlock.Miner.Value,
75 +
                _validNext.Hash,
76 +
                _validNext.Timestamp.AddSeconds(1),
77 +
                _emptyTransaction);
78 +
            Assert.Throws<InvalidBlockTotalDifficultyException>(() =>
79 +
                    _blockChain.Append(invalidTotalDifficultyBlock));
80 +
        }
81 +
82 +
        [Fact]
83 +
        private void ValidateNextBlockInvalidPreviousHash()
84 +
        {
85 +
            _blockChain.Append(_validNext);
86 +
87 +
            var invalidPreviousHashBlock = Block<DumbAction>.Mine(
88 +
                2,
89 +
                1032,
90 +
                _validNext.TotalDifficulty,
91 +
                _fx.GenesisBlock.Miner.Value,
92 +
                new HashDigest<SHA256>(new byte[32]),
93 +
                _validNext.Timestamp.AddSeconds(1),
94 +
                _emptyTransaction);
95 +
            Assert.Throws<InvalidBlockPreviousHashException>(() =>
96 +
                    _blockChain.Append(invalidPreviousHashBlock));
97 +
        }
98 +
99 +
        [Fact]
100 +
        private void ValidateNextBlockInvalidTimestamp()
101 +
        {
102 +
            _blockChain.Append(_validNext);
103 +
104 +
            var invalidPreviousTimestamp = Block<DumbAction>.Mine(
105 +
                2,
106 +
                1032,
107 +
                _validNext.TotalDifficulty,
108 +
                _fx.GenesisBlock.Miner.Value,
109 +
                _validNext.Hash,
110 +
                _validNext.Timestamp.Subtract(TimeSpan.FromSeconds(1)),
111 +
                _emptyTransaction);
112 +
            Assert.Throws<InvalidBlockTimestampException>(() =>
113 +
                    _blockChain.Append(invalidPreviousTimestamp));
114 +
        }
115 +
116 +
        [Fact]
117 +
        private void ValidateNextBlockInvalidStateRootHash()
118 +
        {
119 +
            IKeyValueStore stateKeyValueStore = new MemoryKeyValueStore(),
120 +
                stateHashKeyValueStore = new MemoryKeyValueStore();
121 +
            var policy = new BlockPolicy<DumbAction>(
122 +
                null,
123 +
                TimeSpan.FromHours(3),
124 +
                1024,
125 +
                128);
126 +
            var stateStore = new TrieStateStore(stateKeyValueStore, stateHashKeyValueStore);
127 +
            // FIXME: It assumes that _fx.GenesisBlock doesn't update any states with transactions.
128 +
            //        Actually, it depends on BlockChain<T> to update states and it makes hard to
129 +
            //        calculate state root hash. To resolve this problem,
130 +
            //        it should be moved into StateStore.
131 +
            var genesisBlock = TestUtils.MineGenesis<DumbAction>(
132 +
                blockAction: policy.BlockAction, checkStateRootHash: true);
133 +
            var store = new DefaultStore(null);
134 +
            var chain = new BlockChain<DumbAction>(
135 +
                policy,
136 +
                store,
137 +
                stateStore,
138 +
                genesisBlock);
139 +
140 +
            var validNext = Block<DumbAction>.Mine(
141 +
                1,
142 +
                1024,
143 +
                genesisBlock.TotalDifficulty,
144 +
                genesisBlock.Miner.Value,
145 +
                genesisBlock.Hash,
146 +
                genesisBlock.Timestamp.AddSeconds(1),
147 +
                _emptyTransaction);
148 +
            chain.ExecuteActions(validNext);
149 +
            validNext =
150 +
                new Block<DumbAction>(validNext, stateStore.GetRootHash(validNext.Hash));
151 +
            chain.Append(validNext);
152 +
153 +
            var invalidStateRootHash = Block<DumbAction>.Mine(
154 +
                2,
155 +
                1032,
156 +
                validNext.TotalDifficulty,
157 +
                genesisBlock.Miner.Value,
158 +
                validNext.Hash,
159 +
                validNext.Timestamp.AddSeconds(1),
160 +
                _emptyTransaction);
161 +
            var actionEvaluations = _blockChain.BlockEvaluator.EvaluateActions(
162 +
                invalidStateRootHash,
163 +
                StateCompleterSet<DumbAction>.Recalculate);
164 +
            chain.SetStates(invalidStateRootHash, actionEvaluations, false);
165 +
            invalidStateRootHash = new Block<DumbAction>(
166 +
                invalidStateRootHash,
167 +
                new HashDigest<SHA256>(TestUtils.GetRandomBytes(HashDigest<SHA256>.Size)));
168 +
            Assert.Throws<InvalidBlockStateRootHashException>(() =>
169 +
                    chain.Append(invalidStateRootHash));
170 +
        }
171 +
    }
172 +
}

@@ -807,11 +807,11 @@
Loading
807 807
            );
808 808
809 809
            var chain = TestUtils.MakeBlockChain(new NullPolicy<DumbAction>(), Fx.Store);
810 -
            chain.Append(Fx.Block1);
810 +
            var block = chain.MineBlock(address).Result;
811 811
812 812
            // Even if state references in a chain are empty it should not throw
813 813
            // ChainIdNotFoundException unless the chain in itself does not exist.
814 -
            Fx.BlockStatesStore.ForkStateReferences(chain.Id, targetChainId, Fx.Block1);
814 +
            Fx.BlockStatesStore.ForkStateReferences(chain.Id, targetChainId, block);
815 815
        }
816 816
817 817
        [SkippableFact]

@@ -1,9 +1,6 @@
Loading
1 1
using System;
2 -
using System.Numerics;
3 -
using System.Security.Cryptography;
4 2
using Libplanet.Action;
5 3
using Libplanet.Blocks;
6 -
using Libplanet.Store;
7 4
using Libplanet.Tx;
8 5
9 6
namespace Libplanet.Blockchain.Policies
@@ -124,7 +121,8 @@
Loading
124 121
125 122
        private int DifficultyBoundDivisor { get; }
126 123
127 -
        public bool DoesTransactionFollowsPolicy(Transaction<T> transaction)
124 +
        /// <inheritdoc cref="IBlockPolicy{T}.DoesTransactionFollowsPolicy(Transaction{T})"/>
125 +
        public virtual bool DoesTransactionFollowsPolicy(Transaction<T> transaction)
128 126
        {
129 127
            return _doesTransactionFollowPolicy(transaction);
130 128
        }
@@ -132,94 +130,15 @@
Loading
132 130
        /// <inheritdoc/>
133 131
        /// <exception cref="InvalidBlockStateRootHashException">It will be thrown when the
134 132
        /// given block has incorrect <see cref="Block{T}.StateRootHash"/>.</exception>
135 -
        public InvalidBlockException ValidateNextBlock(
133 +
        public virtual InvalidBlockException ValidateNextBlock(
136 134
            BlockChain<T> blocks,
137 135
            Block<T> nextBlock)
138 136
        {
139 -
            long index = blocks.Count;
140 -
            long difficulty = GetNextBlockDifficulty(blocks);
141 -
            BigInteger totalDifficulty = index >= 1
142 -
                    ? blocks[index - 1].TotalDifficulty + nextBlock.Difficulty
143 -
                    : nextBlock.Difficulty;
144 -
145 -
            Block<T> lastBlock = index >= 1 ? blocks[index - 1] : null;
146 -
            HashDigest<SHA256>? prevHash = lastBlock?.Hash;
147 -
            DateTimeOffset? prevTimestamp = lastBlock?.Timestamp;
148 -
149 -
            if (nextBlock.Index != index)
150 -
            {
151 -
                return new InvalidBlockIndexException(
152 -
                    $"the expected block index is {index}, but its index" +
153 -
                    $" is {nextBlock.Index}'");
154 -
            }
155 -
156 -
            if (nextBlock.Difficulty < difficulty)
157 -
            {
158 -
                return new InvalidBlockDifficultyException(
159 -
                    $"the expected difficulty of the block #{index} " +
160 -
                    $"is {difficulty}, but its difficulty is " +
161 -
                    $"{nextBlock.Difficulty}'");
162 -
            }
163 -
164 -
            if (nextBlock.TotalDifficulty != totalDifficulty)
165 -
            {
166 -
                var msg = $"The expected total difficulty of the block #{index} " +
167 -
                          $"is {totalDifficulty}, but its difficulty is " +
168 -
                          $"{nextBlock.TotalDifficulty}'";
169 -
                return new InvalidBlockTotalDifficultyException(
170 -
                    nextBlock.Difficulty,
171 -
                    nextBlock.TotalDifficulty,
172 -
                    msg);
173 -
            }
174 -
175 -
            if (!nextBlock.PreviousHash.Equals(prevHash))
176 -
            {
177 -
                if (prevHash is null)
178 -
                {
179 -
                    return new InvalidBlockPreviousHashException(
180 -
                        "the genesis block must have not previous block");
181 -
                }
182 -
183 -
                return new InvalidBlockPreviousHashException(
184 -
                    $"the block #{index} is not continuous from the " +
185 -
                    $"block #{index - 1}; while previous block's hash is " +
186 -
                    $"{prevHash}, the block #{index}'s pointer to " +
187 -
                    "the previous hash refers to " +
188 -
                    (nextBlock.PreviousHash?.ToString() ?? "nothing"));
189 -
            }
190 -
191 -
            if (nextBlock.Timestamp < prevTimestamp)
192 -
            {
193 -
                return new InvalidBlockTimestampException(
194 -
                    $"the block #{index}'s timestamp " +
195 -
                    $"({nextBlock.Timestamp}) is earlier than" +
196 -
                    $" the block #{index - 1}'s ({prevTimestamp})");
197 -
            }
198 -
199 -
            // FIXME: receive validation conditions on the constructor and
200 -
            //        extract type testing codes into it.
201 -
            if (blocks.StateStore is TrieStateStore trieStateStore)
202 -
            {
203 -
                blocks.ExecuteActions(nextBlock, StateCompleterSet<T>.Recalculate);
204 -
                HashDigest<SHA256> rootHash =
205 -
                    trieStateStore.GetRootHash(nextBlock.Hash);
206 -
                if (!rootHash.Equals(nextBlock.StateRootHash))
207 -
                {
208 -
                    var message = $"the block #{index}'s state root hash is " +
209 -
                                  $"{nextBlock.StateRootHash?.ToString()}, but the execution " +
210 -
                                  $"result is {rootHash.ToString()}";
211 -
                    return new InvalidBlockStateRootHashException(
212 -
                        nextBlock.StateRootHash,
213 -
                        rootHash,
214 -
                        message);
215 -
                }
216 -
            }
217 -
218 137
            return null;
219 138
        }
220 139
221 140
        /// <inheritdoc />
222 -
        public long GetNextBlockDifficulty(BlockChain<T> blocks)
141 +
        public virtual long GetNextBlockDifficulty(BlockChain<T> blocks)
223 142
        {
224 143
            long index = blocks.Count;
225 144
Files Coverage
Libplanet 86.73%
Libplanet.RocksDBStore 95.77%
Libplanet.RocksDBStore.Tests 77.46%
Libplanet.Stun.Tests/Stun 99.56%
Libplanet.Stun/Stun 77.88%
Libplanet.Tests 90.30%
Project Totals (330 files) 88.92%
1
coverage:
2
  range: 70..95
3
  status:
4
    project:
5
      default:
6
        target: auto
7
        threshold: "1"
8
        base: auto
9

10
comment:
11
  require_changes: true
12
  layout: "diff, files"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading