@@ -179,7 +179,8 @@
Loading
179 179
        public static Block<T> MineGenesis<T>(
180 180
            Address? miner = null,
181 181
            IEnumerable<Transaction<T>> transactions = null,
182 -
            DateTimeOffset? timestamp = null
182 +
            DateTimeOffset? timestamp = null,
183 +
            int protocolVersion = Block<T>.CurrentProtocolVersion
183 184
        )
184 185
            where T : IAction, new()
185 186
        {
@@ -196,7 +197,8 @@
Loading
196 197
                miner: miner ?? GenesisMinerAddress,
197 198
                previousHash: null,
198 199
                timestamp: timestamp ?? new DateTimeOffset(2018, 11, 29, 0, 0, 0, TimeSpan.Zero),
199 -
                transactions: transactions
200 +
                transactions: transactions,
201 +
                protocolVersion: protocolVersion
200 202
            );
201 203
202 204
            return block;
@@ -208,7 +210,8 @@
Loading
208 210
            byte[] nonce = null,
209 211
            long difficulty = 1,
210 212
            Address? miner = null,
211 -
            TimeSpan? blockInterval = null
213 +
            TimeSpan? blockInterval = null,
214 +
            int protocolVersion = Block<T>.CurrentProtocolVersion
212 215
        )
213 216
            where T : IAction, new()
214 217
        {
@@ -232,7 +235,8 @@
Loading
232 235
                    miner: miner ?? previousBlock.Miner.Value,
233 236
                    previousHash: previousHash,
234 237
                    timestamp: timestamp,
235 -
                    transactions: txs
238 +
                    transactions: txs,
239 +
                    protocolVersion: protocolVersion
236 240
                );
237 241
            }
238 242
            else
@@ -245,7 +249,8 @@
Loading
245 249
                    miner: miner ?? previousBlock.Miner.Value,
246 250
                    previousHash: previousHash,
247 251
                    timestamp: timestamp,
248 -
                    transactions: txs
252 +
                    transactions: txs,
253 +
                    protocolVersion: protocolVersion
249 254
                );
250 255
            }
251 256
@@ -312,7 +317,8 @@
Loading
312 317
            PrivateKey privateKey = null,
313 318
            DateTimeOffset? timestamp = null,
314 319
            IEnumerable<IRenderer<T>> renderers = null,
315 -
            Block<T> genesisBlock = null
320 +
            Block<T> genesisBlock = null,
321 +
            int protocolVersion = Block<T>.CurrentProtocolVersion
316 322
        )
317 323
            where T : IAction, new()
318 324
        {
@@ -341,7 +347,9 @@
Loading
341 347
                GenesisMinerAddress,
342 348
                null,
343 349
                timestamp ?? DateTimeOffset.MinValue,
344 -
                new[] { tx, });
350 +
                new[] { tx },
351 +
                protocolVersion: protocolVersion
352 +
            );
345 353
            genesisBlock = genesisBlock.AttachStateRootHash(stateStore, policy.BlockAction);
346 354
            ValidatingActionRenderer<T> validator = null;
347 355
#pragma warning disable S1121

@@ -0,0 +1,49 @@
Loading
1 +
#nullable enable
2 +
using System;
3 +
using System.Runtime.Serialization;
4 +
5 +
namespace Libplanet.Blocks
6 +
{
7 +
    /// <summary>
8 +
    /// The exception that is thrown when a <see cref="Block{T}"/>'s
9 +
    /// <see cref="Block{T}.ProtocolVersion"/> (or a <see cref="BlockHeader"/>'s
10 +
    /// <see cref="BlockHeader.ProtocolVersion"/>) is invalid.
11 +
    /// </summary>
12 +
    [Serializable]
13 +
    public sealed class InvalidBlockProtocolVersionException : InvalidBlockException, ISerializable
14 +
    {
15 +
        /// <summary>
16 +
        /// Initializes a new instance of <see cref="InvalidBlockProtocolVersionException"/> class.
17 +
        /// </summary>
18 +
        /// <param name="actualProtocolVersion">The actual block protocol version which is invalid.
19 +
        /// </param>
20 +
        /// <param name="message">The message that describes the error.</param>
21 +
        public InvalidBlockProtocolVersionException(int actualProtocolVersion, string message)
22 +
            : base(message)
23 +
        {
24 +
            ActualProtocolVersion = actualProtocolVersion;
25 +
        }
26 +
27 +
        private InvalidBlockProtocolVersionException(
28 +
            SerializationInfo info,
29 +
            StreamingContext context
30 +
        )
31 +
            : base(info.GetString(nameof(Message)) ?? string.Empty)
32 +
        {
33 +
            ActualProtocolVersion = info.GetInt32(nameof(ActualProtocolVersion));
34 +
        }
35 +
36 +
        /// <summary>
37 +
        /// The actual block protocol version which is invalid.
38 +
        /// </summary>
39 +
        public int ActualProtocolVersion { get; set; }
40 +
41 +
        /// <inheritdoc cref="Exception.GetObjectData(SerializationInfo, StreamingContext)"/>
42 +
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
43 +
        {
44 +
            base.GetObjectData(info, context);
45 +
            info.AddValue(nameof(Message), Message);
46 +
            info.AddValue(nameof(ActualProtocolVersion), ActualProtocolVersion);
47 +
        }
48 +
    }
49 +
}

@@ -1935,6 +1935,27 @@
Loading
1935 1935
1936 1936
        private InvalidBlockException ValidateNextBlock(Block<T> nextBlock)
1937 1937
        {
1938 +
            int actualProtocolVersion = nextBlock.ProtocolVersion;
1939 +
            const int currentProtocolVersion = Block<T>.CurrentProtocolVersion;
1940 +
            if (actualProtocolVersion > currentProtocolVersion)
1941 +
            {
1942 +
                string message =
1943 +
                    $"The protocol version ({actualProtocolVersion}) of the block " +
1944 +
                    $"#{nextBlock.Index} {nextBlock.Hash} is not supported by this node." +
1945 +
                    $"The highest supported protocol version is {currentProtocolVersion}.";
1946 +
                throw new InvalidBlockProtocolVersionException(
1947 +
                    actualProtocolVersion,
1948 +
                    message
1949 +
                );
1950 +
            }
1951 +
            else if (Tip is { } tip && actualProtocolVersion < tip.ProtocolVersion)
1952 +
            {
1953 +
                string message =
1954 +
                    "The protocol version is disallowed to be downgraded from the topmost block " +
1955 +
                    $"in the chain ({actualProtocolVersion} < {tip.ProtocolVersion}).";
1956 +
                throw new InvalidBlockProtocolVersionException(actualProtocolVersion, message);
1957 +
            }
1958 +
1938 1959
            InvalidBlockException e = Policy.ValidateNextBlock(this, nextBlock);
1939 1960
1940 1961
            if (!(e is null))

@@ -9,13 +9,19 @@
Loading
9 9
{
10 10
    public class BlockFixture
11 11
    {
12 +
        public const int ProtocolVersion =
13 +
            Block<PolymorphicAction<BaseAction>>.CurrentProtocolVersion;
14 +
12 15
        public BlockFixture()
13 16
        {
14 -
            Genesis = TestUtils.MineGenesis<PolymorphicAction<BaseAction>>();
17 +
            Genesis = TestUtils.MineGenesis<PolymorphicAction<BaseAction>>(
18 +
                protocolVersion: ProtocolVersion
19 +
            );
15 20
            TxFixture = new TxFixture(Genesis.Hash);
16 21
            Next = TestUtils.MineNext(
17 22
                Genesis,
18 -
                nonce: new byte[] { 0x02, 0x00, 0x00, 0x00 }
23 +
                nonce: new byte[] { 0x02, 0x00, 0x00, 0x00 },
24 +
                protocolVersion: ProtocolVersion
19 25
            );
20 26
            byte[] hasTxNonce =
21 27
            {
@@ -27,7 +33,8 @@
Loading
27 33
                {
28 34
                    TxFixture.TxWithActions,
29 35
                },
30 -
                hasTxNonce
36 +
                hasTxNonce,
37 +
                protocolVersion: ProtocolVersion
31 38
            );
32 39
        }
33 40

@@ -16,18 +16,59 @@
Loading
16 16
        [Fact]
17 17
        public void ValidateNextBlock()
18 18
        {
19 -
            var validNextBlock = Block<DumbAction>.Mine(
19 +
            Block<DumbAction> validNextBlock = Block<DumbAction>.Mine(
20 20
                1,
21 21
                1024,
22 22
                _fx.GenesisBlock.TotalDifficulty,
23 23
                _fx.GenesisBlock.Miner.Value,
24 24
                _fx.GenesisBlock.Hash,
25 25
                _fx.GenesisBlock.Timestamp.AddDays(1),
26 -
                _emptyTransaction).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
26 +
                _emptyTransaction
27 +
            ).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
27 28
            _blockChain.Append(validNextBlock);
28 29
            Assert.Equal(_blockChain.Tip, validNextBlock);
29 30
        }
30 31
32 +
        [Fact]
33 +
        private void ValidateNextBlockProtocolVersion()
34 +
        {
35 +
            Block<DumbAction> block1 = Block<DumbAction>.Mine(
36 +
                1,
37 +
                1024,
38 +
                _fx.GenesisBlock.TotalDifficulty,
39 +
                _fx.GenesisBlock.Miner.Value,
40 +
                _fx.GenesisBlock.Hash,
41 +
                _fx.GenesisBlock.Timestamp.AddDays(1),
42 +
                _emptyTransaction,
43 +
                protocolVersion: 1
44 +
            ).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
45 +
            _blockChain.Append(block1);
46 +
47 +
            Block<DumbAction> block2 = Block<DumbAction>.Mine(
48 +
                2,
49 +
                1024,
50 +
                block1.TotalDifficulty,
51 +
                _fx.GenesisBlock.Miner.Value,
52 +
                block1.Hash,
53 +
                _fx.GenesisBlock.Timestamp.AddDays(1),
54 +
                _emptyTransaction,
55 +
                protocolVersion: 0
56 +
            ).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
57 +
            Assert.Throws<InvalidBlockProtocolVersionException>(() => _blockChain.Append(block2));
58 +
59 +
            Block<DumbAction> block3 = Block<DumbAction>.Mine(
60 +
                2,
61 +
                1024,
62 +
                block1.TotalDifficulty,
63 +
                _fx.GenesisBlock.Miner.Value,
64 +
                block1.Hash,
65 +
                _fx.GenesisBlock.Timestamp.AddDays(1),
66 +
                _emptyTransaction,
67 +
                protocolVersion: Block<DumbAction>.CurrentProtocolVersion + 1
68 +
            ).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
69 +
            Assert.Throws<InvalidBlockProtocolVersionException>(() => _blockChain.Append(block3));
70 +
        }
71 +
31 72
        [Fact]
32 73
        private void ValidateNextBlockInvalidIndex()
33 74
        {

@@ -1,6 +1,8 @@
Loading
1 1
using System;
2 2
using System.Collections.Immutable;
3 3
using System.Globalization;
4 +
using System.Linq;
5 +
using Bencodex.Types;
4 6
using Libplanet.Blocks;
5 7
using Xunit;
6 8
@@ -12,6 +14,123 @@
Loading
12 14
13 15
        public BlockHeaderTest(BlockFixture fixture) => _fx = fixture;
14 16
17 +
        [Fact]
18 +
        public void ToBencodex()
19 +
        {
20 +
            var header = new BlockHeader(
21 +
                protocolVersion: 0,
22 +
                index: 0,
23 +
                difficulty: _fx.Genesis.Difficulty,
24 +
                totalDifficulty: _fx.Genesis.TotalDifficulty,
25 +
                nonce: _fx.Genesis.Nonce.ByteArray,
26 +
                miner: _fx.Genesis.Miner?.ByteArray ?? ImmutableArray<byte>.Empty,
27 +
                hash: _fx.Genesis.Hash.ByteArray,
28 +
                txHash: _fx.Genesis.TxHash?.ByteArray ?? ImmutableArray<byte>.Empty,
29 +
                previousHash: _fx.Genesis.PreviousHash?.ByteArray ?? ImmutableArray<byte>.Empty,
30 +
                timestamp: _fx.Genesis.Timestamp.ToString(
31 +
                    BlockHeader.TimestampFormat,
32 +
                    CultureInfo.InvariantCulture
33 +
                ),
34 +
                preEvaluationHash: _fx.Genesis.PreEvaluationHash.ByteArray,
35 +
                stateRootHash: _fx.Genesis.StateRootHash?.ByteArray ?? ImmutableArray<byte>.Empty
36 +
            );
37 +
            Bencodex.Types.Dictionary expected = Bencodex.Types.Dictionary.Empty
38 +
                .Add(BlockHeader.IndexKey, 0)
39 +
                .Add(
40 +
                    BlockHeader.TimestampKey,
41 +
                    _fx.Genesis.Timestamp.ToString(
42 +
                        BlockHeader.TimestampFormat,
43 +
                        CultureInfo.InvariantCulture
44 +
                    )
45 +
                )
46 +
                .Add(BlockHeader.DifficultyKey, _fx.Genesis.Difficulty)
47 +
                .Add(
48 +
                    BlockHeader.TotalDifficultyKey,
49 +
                    (IValue)new Bencodex.Types.Integer(_fx.Genesis.TotalDifficulty)
50 +
                )
51 +
                .Add(BlockHeader.NonceKey, _fx.Genesis.Nonce.ToByteArray())
52 +
                .Add(BlockHeader.HashKey, _fx.Genesis.Hash.ToByteArray())
53 +
                .Add(BlockHeader.MinerKey, _fx.Genesis.Miner?.ToByteArray() ?? new byte[0])
54 +
                .Add(BlockHeader.PreEvaluationHashKey, _fx.Genesis.PreEvaluationHash.ToByteArray());
55 +
            Assert.Equal(expected, header.ToBencodex());
56 +
            Assert.Equal(expected, new BlockHeader(expected).ToBencodex());
57 +
58 +
            ImmutableArray<byte> randomHash = TestUtils.GetRandomBytes(32).ToImmutableArray();
59 +
            ImmutableArray<byte> randomHash2 = TestUtils.GetRandomBytes(32).ToImmutableArray();
60 +
            header = new BlockHeader(
61 +
                protocolVersion: 1,
62 +
                index: 1,
63 +
                difficulty: _fx.Next.Difficulty,
64 +
                totalDifficulty: _fx.Next.TotalDifficulty,
65 +
                nonce: _fx.Next.Nonce.ByteArray,
66 +
                miner: ImmutableArray<byte>.Empty,
67 +
                hash: _fx.Next.Hash.ByteArray,
68 +
                txHash: randomHash,
69 +
                previousHash: _fx.Genesis.Hash.ByteArray,
70 +
                timestamp: _fx.Next.Timestamp.ToString(
71 +
                    BlockHeader.TimestampFormat,
72 +
                    CultureInfo.InvariantCulture
73 +
                ),
74 +
                preEvaluationHash: ImmutableArray<byte>.Empty,
75 +
                stateRootHash: randomHash2
76 +
            );
77 +
            expected = Bencodex.Types.Dictionary.Empty
78 +
                .Add(BlockHeader.ProtocolVersionKey, 1)
79 +
                .Add(BlockHeader.IndexKey, 1)
80 +
                .Add(
81 +
                    BlockHeader.TimestampKey,
82 +
                    _fx.Next.Timestamp.ToString(
83 +
                        BlockHeader.TimestampFormat,
84 +
                        CultureInfo.InvariantCulture
85 +
                    )
86 +
                )
87 +
                .Add(BlockHeader.DifficultyKey, _fx.Next.Difficulty)
88 +
                .Add(
89 +
                    BlockHeader.TotalDifficultyKey,
90 +
                    (IValue)new Bencodex.Types.Integer(_fx.Next.TotalDifficulty)
91 +
                )
92 +
                .Add(BlockHeader.NonceKey, _fx.Next.Nonce.ToByteArray())
93 +
                .Add(BlockHeader.HashKey, _fx.Next.Hash.ToByteArray())
94 +
                .Add(BlockHeader.PreviousHashKey, _fx.Genesis.Hash.ToByteArray())
95 +
                .Add(BlockHeader.TxHashKey, randomHash.ToArray())
96 +
                .Add(BlockHeader.StateRootHashKey, randomHash2.ToArray());
97 +
            Assert.Equal(expected, header.ToBencodex());
98 +
            Assert.Equal(expected, new BlockHeader(expected).ToBencodex());
99 +
        }
100 +
101 +
        [Fact]
102 +
        public void ValidateValidHeader()
103 +
        {
104 +
            _fx.Genesis.Header.Validate(DateTimeOffset.UtcNow);
105 +
            _fx.Next.Header.Validate(DateTimeOffset.UtcNow);
106 +
        }
107 +
108 +
        [Fact]
109 +
        public void ValidateProtocolVersion()
110 +
        {
111 +
            var header = new BlockHeader(
112 +
                protocolVersion: -1,
113 +
                index: 0,
114 +
                difficulty: _fx.Next.Difficulty,
115 +
                totalDifficulty: _fx.Next.TotalDifficulty,
116 +
                nonce: _fx.Next.Nonce.ByteArray,
117 +
                miner: _fx.Next.Miner?.ByteArray ?? ImmutableArray<byte>.Empty,
118 +
                hash: _fx.Next.Hash.ByteArray,
119 +
                txHash: _fx.Next.TxHash?.ByteArray ?? ImmutableArray<byte>.Empty,
120 +
                previousHash: _fx.Next.PreviousHash?.ByteArray ?? ImmutableArray<byte>.Empty,
121 +
                timestamp: _fx.Next.Timestamp.ToString(
122 +
                    BlockHeader.TimestampFormat,
123 +
                    CultureInfo.InvariantCulture
124 +
                ),
125 +
                preEvaluationHash: TestUtils.GetRandomBytes(32).ToImmutableArray(),
126 +
                stateRootHash: ImmutableArray<byte>.Empty
127 +
            );
128 +
129 +
            Assert.Throws<InvalidBlockProtocolVersionException>(() =>
130 +
                header.Validate(DateTimeOffset.UtcNow)
131 +
            );
132 +
        }
133 +
15 134
        [Fact]
16 135
        public void ValidateTimestamp()
17 136
        {
@@ -19,6 +138,7 @@
Loading
19 138
            string future = (now + TimeSpan.FromSeconds(16))
20 139
                .ToString(BlockHeader.TimestampFormat, CultureInfo.InvariantCulture);
21 140
            var header = new BlockHeader(
141 +
                protocolVersion: 0,
22 142
                index: 0,
23 143
                difficulty: 0,
24 144
                totalDifficulty: 0,
@@ -43,6 +163,7 @@
Loading
43 163
        public void ValidateNonce()
44 164
        {
45 165
            var header = new BlockHeader(
166 +
                protocolVersion: 0,
46 167
                index: _fx.Next.Index,
47 168
                difficulty: long.MaxValue,
48 169
                totalDifficulty: _fx.Genesis.TotalDifficulty + long.MaxValue,
@@ -67,6 +188,7 @@
Loading
67 188
        public void ValidateIndex()
68 189
        {
69 190
            var header = new BlockHeader(
191 +
                protocolVersion: 0,
70 192
                index: -1,
71 193
                difficulty: _fx.Next.Difficulty,
72 194
                totalDifficulty: _fx.Next.TotalDifficulty,
@@ -92,6 +214,7 @@
Loading
92 214
        {
93 215
            DateTimeOffset now = DateTimeOffset.UtcNow;
94 216
            var genesisHeader = new BlockHeader(
217 +
                protocolVersion: 0,
95 218
                index: 0,
96 219
                difficulty: 1000,
97 220
                totalDifficulty: 1000,
@@ -109,6 +232,7 @@
Loading
109 232
                genesisHeader.Validate(DateTimeOffset.UtcNow));
110 233
111 234
            var header1 = new BlockHeader(
235 +
                protocolVersion: 0,
112 236
                index: 10,
113 237
                difficulty: 0,
114 238
                totalDifficulty: 1000,
@@ -126,6 +250,7 @@
Loading
126 250
                header1.Validate(DateTimeOffset.UtcNow));
127 251
128 252
            var header2 = new BlockHeader(
253 +
                protocolVersion: 0,
129 254
                index: 10,
130 255
                difficulty: 1000,
131 256
                totalDifficulty: 10,
@@ -148,6 +273,7 @@
Loading
148 273
        {
149 274
            DateTimeOffset now = DateTimeOffset.UtcNow;
150 275
            var genesisHeader = new BlockHeader(
276 +
                protocolVersion: 0,
151 277
                index: 0,
152 278
                difficulty: 0,
153 279
                totalDifficulty: 0,
@@ -165,6 +291,7 @@
Loading
165 291
                genesisHeader.Validate(DateTimeOffset.UtcNow));
166 292
167 293
            var header = new BlockHeader(
294 +
                protocolVersion: 0,
168 295
                index: 10,
169 296
                difficulty: 1000,
170 297
                totalDifficulty: 1500,

@@ -73,7 +73,7 @@
Loading
73 73
        }
74 74
75 75
        [Fact]
76 -
        public void CanMine()
76 +
        public void Mine()
77 77
        {
78 78
            Assert.Equal(0, _fx.Genesis.Index);
79 79
            Assert.Equal(0, _fx.Genesis.Difficulty);
@@ -90,9 +90,9 @@
Loading
90 90
                new HashDigest<SHA256>(
91 91
                    new byte[]
92 92
                    {
93 -
                        0xd4, 0xf3, 0x58, 0x34, 0xe2, 0x7d, 0x5a, 0xb4, 0x59, 0xa4, 0xd4, 0x01,
94 -
                        0xe9, 0xa0, 0x82, 0x68, 0xe3, 0xfe, 0x32, 0x1b, 0x8c, 0x68, 0x50, 0x75,
95 -
                        0xae, 0xc5, 0xbd, 0x5d, 0x18, 0xd6, 0x42, 0xaa,
93 +
                        0xd6, 0x93, 0xda, 0x38, 0x66, 0xa3, 0x4d, 0x65, 0x9e, 0x01, 0x4f, 0x97,
94 +
                        0xc8, 0xfe, 0xb0, 0x8a, 0xfe, 0x2e, 0x97, 0xc9, 0x9e, 0x3f, 0x33, 0x89,
95 +
                        0xda, 0x02, 0x5f, 0xd0, 0x66, 0x5c, 0x62, 0x1c,
96 96
                    }
97 97
                ),
98 98
                _fx.Genesis.Hash
@@ -117,20 +117,19 @@
Loading
117 117
        {
118 118
            byte[] expected =
119 119
            {
120 -
                0x64, 0x31, 0x3a, 0x48, 0x64, 0x31, 0x3a, 0x54, 0x69, 0x30, 0x65, 0x31, 0x3a,
121 -
                0x63, 0x33, 0x32, 0x3a, 0xd4, 0xf3, 0x58, 0x34, 0xe2, 0x7d, 0x5a, 0xb4, 0x59,
122 -
                0xa4, 0xd4, 0x01, 0xe9, 0xa0, 0x82, 0x68, 0xe3, 0xfe, 0x32, 0x1b, 0x8c, 0x68,
123 -
                0x50, 0x75, 0xae, 0xc5, 0xbd, 0x5d, 0x18, 0xd6, 0x42, 0xaa, 0x31, 0x3a, 0x64,
124 -
                0x69, 0x30, 0x65, 0x31, 0x3a, 0x68, 0x33, 0x32, 0x3a, 0xd4, 0xf3, 0x58, 0x34,
125 -
                0xe2, 0x7d, 0x5a, 0xb4, 0x59, 0xa4, 0xd4, 0x01, 0xe9, 0xa0, 0x82, 0x68, 0xe3,
126 -
                0xfe, 0x32, 0x1b, 0x8c, 0x68, 0x50, 0x75, 0xae, 0xc5, 0xbd, 0x5d, 0x18, 0xd6,
127 -
                0x42, 0xaa, 0x31, 0x3a, 0x69, 0x69, 0x30, 0x65, 0x31, 0x3a, 0x6d, 0x32, 0x30,
128 -
                0x3a, 0x21, 0x74, 0x4f, 0x4f, 0x08, 0xdb, 0x23, 0xe0, 0x44, 0x17, 0x8d, 0xaf,
129 -
                0xb8, 0x27, 0x3a, 0xeb, 0x5e, 0xbe, 0x66, 0x44, 0x31, 0x3a, 0x6e, 0x34, 0x3a,
130 -
                0x01, 0x00, 0x00, 0x00, 0x31, 0x3a, 0x74, 0x75, 0x32, 0x37, 0x3a, 0x32, 0x30,
131 -
                0x31, 0x38, 0x2d, 0x31, 0x31, 0x2d, 0x32, 0x39, 0x54, 0x30, 0x30, 0x3a, 0x30,
132 -
                0x30, 0x3a, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x65,
133 -
                0x65,
120 +
                0x64, 0x31, 0x3a, 0x48, 0x64, 0x31, 0x3a, 0x00, 0x69, 0x31, 0x65, 0x31, 0x3a, 0x54,
121 +
                0x69, 0x30, 0x65, 0x31, 0x3a, 0x63, 0x33, 0x32, 0x3a, 0xd6, 0x93, 0xda, 0x38, 0x66,
122 +
                0xa3, 0x4d, 0x65, 0x9e, 0x01, 0x4f, 0x97, 0xc8, 0xfe, 0xb0, 0x8a, 0xfe, 0x2e, 0x97,
123 +
                0xc9, 0x9e, 0x3f, 0x33, 0x89, 0xda, 0x02, 0x5f, 0xd0, 0x66, 0x5c, 0x62, 0x1c, 0x31,
124 +
                0x3a, 0x64, 0x69, 0x30, 0x65, 0x31, 0x3a, 0x68, 0x33, 0x32, 0x3a, 0xd6, 0x93, 0xda,
125 +
                0x38, 0x66, 0xa3, 0x4d, 0x65, 0x9e, 0x01, 0x4f, 0x97, 0xc8, 0xfe, 0xb0, 0x8a, 0xfe,
126 +
                0x2e, 0x97, 0xc9, 0x9e, 0x3f, 0x33, 0x89, 0xda, 0x02, 0x5f, 0xd0, 0x66, 0x5c, 0x62,
127 +
                0x1c, 0x31, 0x3a, 0x69, 0x69, 0x30, 0x65, 0x31, 0x3a, 0x6d, 0x32, 0x30, 0x3a, 0x21,
128 +
                0x74, 0x4f, 0x4f, 0x08, 0xdb, 0x23, 0xe0, 0x44, 0x17, 0x8d, 0xaf, 0xb8, 0x27, 0x3a,
129 +
                0xeb, 0x5e, 0xbe, 0x66, 0x44, 0x31, 0x3a, 0x6e, 0x34, 0x3a, 0x01, 0x00, 0x00, 0x00,
130 +
                0x31, 0x3a, 0x74, 0x75, 0x32, 0x37, 0x3a, 0x32, 0x30, 0x31, 0x38, 0x2d, 0x31, 0x31,
131 +
                0x2d, 0x32, 0x39, 0x54, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2e, 0x30,
132 +
                0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x65, 0x65,
134 133
            };
135 134
136 135
            AssertBytesEqual(expected, _fx.Genesis.Serialize());
@@ -142,56 +141,57 @@
Loading
142 141
        {
143 142
            byte[] encoded =
144 143
            {
145 -
                0x64, 0x31, 0x3a, 0x48, 0x64, 0x31, 0x3a, 0x54, 0x69, 0x32, 0x65, 0x31, 0x3a, 0x63,
146 -
                0x33, 0x32, 0x3a, 0x22, 0xe1, 0x3b, 0xb5, 0x46, 0x65, 0x91, 0x34, 0x28, 0xbc, 0x55,
147 -
                0x86, 0xce, 0xbd, 0x50, 0xdc, 0x25, 0x50, 0xb0, 0x36, 0xa6, 0x18, 0xe9, 0xb0, 0x5d,
148 -
                0xb1, 0x1c, 0x76, 0xad, 0xb0, 0x0a, 0xd7, 0x31, 0x3a, 0x64, 0x69, 0x31, 0x65, 0x31,
149 -
                0x3a, 0x68, 0x33, 0x32, 0x3a, 0x22, 0xe1, 0x3b, 0xb5, 0x46, 0x65, 0x91, 0x34, 0x28,
150 -
                0xbc, 0x55, 0x86, 0xce, 0xbd, 0x50, 0xdc, 0x25, 0x50, 0xb0, 0x36, 0xa6, 0x18, 0xe9,
151 -
                0xb0, 0x5d, 0xb1, 0x1c, 0x76, 0xad, 0xb0, 0x0a, 0xd7, 0x31, 0x3a, 0x69, 0x69, 0x32,
152 -
                0x65, 0x31, 0x3a, 0x6d, 0x32, 0x30, 0x3a, 0x21, 0x74, 0x4f, 0x4f, 0x08, 0xdb, 0x23,
153 -
                0xe0, 0x44, 0x17, 0x8d, 0xaf, 0xb8, 0x27, 0x3a, 0xeb, 0x5e, 0xbe, 0x66, 0x44, 0x31,
154 -
                0x3a, 0x6e, 0x31, 0x30, 0x3a, 0x5c, 0x77, 0x74, 0xc2, 0x39, 0x69, 0x37, 0x51, 0x87,
155 -
                0xa5, 0x31, 0x3a, 0x70, 0x33, 0x32, 0x3a, 0x1c, 0xd4, 0x45, 0x16, 0x24, 0xef, 0x9c,
156 -
                0x79, 0xe2, 0xc2, 0xfb, 0x5a, 0x8e, 0x79, 0x1e, 0x4f, 0xa5, 0x6a, 0x7d, 0x8c, 0x61,
157 -
                0x0c, 0x14, 0xa8, 0xa3, 0x4a, 0xe1, 0x75, 0xb5, 0x20, 0x5c, 0xf7, 0x31, 0x3a, 0x74,
158 -
                0x75, 0x32, 0x37, 0x3a, 0x32, 0x30, 0x31, 0x38, 0x2d, 0x31, 0x31, 0x2d, 0x32, 0x39,
159 -
                0x54, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x33, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30,
160 -
                0x30, 0x30, 0x5a, 0x31, 0x3a, 0x78, 0x33, 0x32, 0x3a, 0x97, 0x3c, 0xa5, 0xff, 0xce,
161 -
                0x89, 0x84, 0xc0, 0x39, 0xe6, 0x3f, 0x2b, 0xee, 0x2f, 0x01, 0x67, 0x00, 0x51, 0x00,
162 -
                0xa0, 0xfb, 0xa4, 0x71, 0x4f, 0xf6, 0xfb, 0x67, 0xe0, 0xaf, 0x61, 0x53, 0x00, 0x65,
163 -
                0x31, 0x3a, 0x54, 0x6c, 0x34, 0x33, 0x35, 0x3a, 0x64, 0x31, 0x3a, 0x53, 0x37, 0x30,
164 -
                0x3a, 0x30, 0x44, 0x02, 0x20, 0x01, 0xb6, 0x10, 0xa7, 0x15, 0xda, 0x89, 0x15, 0x1b,
165 -
                0x5f, 0x62, 0xde, 0xea, 0x12, 0x92, 0xe4, 0x48, 0x80, 0xa3, 0x7d, 0x8a, 0xbe, 0x38,
166 -
                0xd3, 0x03, 0x73, 0x4f, 0x48, 0x91, 0xcd, 0xc9, 0x47, 0x02, 0x20, 0x50, 0x63, 0xb9,
167 -
                0x8f, 0xef, 0xc7, 0x6f, 0x54, 0x53, 0x2c, 0x94, 0x7d, 0x99, 0xc2, 0x0e, 0x1e, 0xd3,
168 -
                0xac, 0xd7, 0xc4, 0x57, 0xed, 0x18, 0x3d, 0x59, 0x02, 0xa8, 0x3a, 0x61, 0xfa, 0x68,
169 -
                0x8f, 0x31, 0x3a, 0x61, 0x6c, 0x64, 0x75, 0x37, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x5f,
170 -
                0x69, 0x64, 0x75, 0x36, 0x3a, 0x61, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x75, 0x36, 0x3a,
171 -
                0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x64, 0x75, 0x36, 0x3a, 0x74, 0x61, 0x72, 0x67,
172 -
                0x65, 0x74, 0x75, 0x33, 0x3a, 0x6f, 0x72, 0x63, 0x75, 0x31, 0x34, 0x3a, 0x74, 0x61,
173 -
                0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x32, 0x30,
174 -
                0x3a, 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d, 0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb,
175 -
                0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc, 0x75, 0x36, 0x3a, 0x77, 0x65, 0x61, 0x70,
176 -
                0x6f, 0x6e, 0x75, 0x34, 0x3a, 0x77, 0x61, 0x6e, 0x64, 0x65, 0x65, 0x64, 0x75, 0x37,
177 -
                0x3a, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x75, 0x35, 0x3a, 0x73, 0x6c, 0x65,
178 -
                0x65, 0x70, 0x75, 0x36, 0x3a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x64, 0x75, 0x37,
179 -
                0x3a, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x69, 0x31, 0x30, 0x65, 0x65, 0x65,
180 -
                0x65, 0x31, 0x3a, 0x67, 0x33, 0x32, 0x3a, 0xd4, 0xf3, 0x58, 0x34, 0xe2, 0x7d, 0x5a,
181 -
                0xb4, 0x59, 0xa4, 0xd4, 0x01, 0xe9, 0xa0, 0x82, 0x68, 0xe3, 0xfe, 0x32, 0x1b, 0x8c,
182 -
                0x68, 0x50, 0x75, 0xae, 0xc5, 0xbd, 0x5d, 0x18, 0xd6, 0x42, 0xaa, 0x31, 0x3a, 0x6e,
183 -
                0x69, 0x30, 0x65, 0x31, 0x3a, 0x70, 0x36, 0x35, 0x3a, 0x04, 0x46, 0x11, 0x5b, 0x01,
184 -
                0x31, 0xba, 0xcc, 0xf9, 0x4a, 0x58, 0x56, 0xed, 0xe8, 0x71, 0x29, 0x5f, 0x6f, 0x3d,
185 -
                0x35, 0x2e, 0x68, 0x47, 0xcd, 0xa9, 0xc0, 0x3e, 0x89, 0xfe, 0x09, 0xf7, 0x32, 0x80,
186 -
                0x87, 0x11, 0xec, 0x97, 0xaf, 0x6e, 0x34, 0x1f, 0x11, 0x0a, 0x32, 0x6d, 0xa1, 0xbd,
187 -
                0xb8, 0x1f, 0x5a, 0xe3, 0xba, 0xdf, 0x76, 0xa9, 0x0b, 0x22, 0xc8, 0xc4, 0x91, 0xae,
188 -
                0xd3, 0xaa, 0xa2, 0x96, 0x31, 0x3a, 0x73, 0x32, 0x30, 0x3a, 0xc2, 0xa8, 0x60, 0x14,
189 -
                0x07, 0x3d, 0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb, 0x54, 0x26, 0x3d, 0xfa, 0x4f,
190 -
                0x5c, 0xbc, 0x31, 0x3a, 0x74, 0x75, 0x32, 0x37, 0x3a, 0x32, 0x30, 0x31, 0x38, 0x2d,
191 -
                0x31, 0x31, 0x2d, 0x32, 0x31, 0x54, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30,
192 -
                0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x31, 0x3a, 0x75, 0x6c, 0x32, 0x30,
193 -
                0x3a, 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d, 0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb,
194 -
                0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc, 0x65, 0x65, 0x65, 0x65,
144 +
                0x64, 0x31, 0x3a, 0x48, 0x64, 0x31, 0x3a, 0x00, 0x69, 0x31, 0x65, 0x31, 0x3a, 0x54,
145 +
                0x69, 0x32, 0x65, 0x31, 0x3a, 0x63, 0x33, 0x32, 0x3a, 0x28, 0x40, 0xe8, 0x6a, 0x27,
146 +
                0xda, 0x6e, 0x0d, 0xe6, 0x2e, 0xab, 0xab, 0x32, 0x95, 0x3e, 0x3b, 0x33, 0xf2, 0xa6,
147 +
                0x51, 0x18, 0x35, 0xf1, 0x85, 0x1a, 0x1a, 0xcc, 0x51, 0xd7, 0x71, 0xcb, 0x99, 0x31,
148 +
                0x3a, 0x64, 0x69, 0x31, 0x65, 0x31, 0x3a, 0x68, 0x33, 0x32, 0x3a, 0x28, 0x40, 0xe8,
149 +
                0x6a, 0x27, 0xda, 0x6e, 0x0d, 0xe6, 0x2e, 0xab, 0xab, 0x32, 0x95, 0x3e, 0x3b, 0x33,
150 +
                0xf2, 0xa6, 0x51, 0x18, 0x35, 0xf1, 0x85, 0x1a, 0x1a, 0xcc, 0x51, 0xd7, 0x71, 0xcb,
151 +
                0x99, 0x31, 0x3a, 0x69, 0x69, 0x32, 0x65, 0x31, 0x3a, 0x6d, 0x32, 0x30, 0x3a, 0x21,
152 +
                0x74, 0x4f, 0x4f, 0x08, 0xdb, 0x23, 0xe0, 0x44, 0x17, 0x8d, 0xaf, 0xb8, 0x27, 0x3a,
153 +
                0xeb, 0x5e, 0xbe, 0x66, 0x44, 0x31, 0x3a, 0x6e, 0x31, 0x30, 0x3a, 0x5c, 0x77, 0x74,
154 +
                0xc2, 0x39, 0x69, 0x37, 0x51, 0x87, 0xa5, 0x31, 0x3a, 0x70, 0x33, 0x32, 0x3a, 0x1b,
155 +
                0xba, 0x9f, 0xcf, 0x4c, 0x81, 0x52, 0xc8, 0x99, 0xed, 0x16, 0x74, 0xec, 0xbf, 0x4a,
156 +
                0x65, 0x71, 0xc2, 0x71, 0x92, 0x2c, 0x08, 0x84, 0xae, 0x80, 0x9f, 0x91, 0xf0, 0x37,
157 +
                0xbe, 0xd8, 0xfc, 0x31, 0x3a, 0x74, 0x75, 0x32, 0x37, 0x3a, 0x32, 0x30, 0x31, 0x38,
158 +
                0x2d, 0x31, 0x31, 0x2d, 0x32, 0x39, 0x54, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x33,
159 +
                0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x31, 0x3a, 0x78, 0x33, 0x32,
160 +
                0x3a, 0x75, 0xa9, 0x2b, 0xf5, 0xcf, 0xf3, 0x5d, 0x5b, 0x4d, 0x0c, 0xb8, 0x40, 0xab,
161 +
                0x61, 0x11, 0x3d, 0xee, 0xab, 0x6f, 0x78, 0x0f, 0x9b, 0x69, 0xfc, 0x48, 0x68, 0xe6,
162 +
                0xba, 0xac, 0xdc, 0xcb, 0x54, 0x65, 0x31, 0x3a, 0x54, 0x6c, 0x34, 0x33, 0x36, 0x3a,
163 +
                0x64, 0x31, 0x3a, 0x53, 0x37, 0x31, 0x3a, 0x30, 0x45, 0x02, 0x21, 0x00, 0xc4, 0xf1,
164 +
                0x85, 0x62, 0xdf, 0x1a, 0x46, 0xb0, 0x0b, 0x9c, 0x50, 0x81, 0x9f, 0x24, 0xaa, 0xf5,
165 +
                0x83, 0xc8, 0x86, 0xaa, 0x82, 0xcc, 0xb9, 0xdf, 0x27, 0x41, 0xd0, 0x21, 0xba, 0x3d,
166 +
                0xf7, 0x18, 0x02, 0x20, 0x7e, 0xb2, 0x5b, 0xbd, 0xaa, 0x3c, 0xc5, 0x2a, 0x36, 0x4c,
167 +
                0x28, 0x77, 0xbb, 0xac, 0x31, 0xb4, 0xa0, 0x22, 0xa2, 0x44, 0x97, 0xaf, 0x90, 0x5b,
168 +
                0x01, 0xff, 0x24, 0x09, 0xea, 0x85, 0x7f, 0x5c, 0x31, 0x3a, 0x61, 0x6c, 0x64, 0x75,
169 +
                0x37, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x75, 0x36, 0x3a, 0x61, 0x74,
170 +
                0x74, 0x61, 0x63, 0x6b, 0x75, 0x36, 0x3a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x64,
171 +
                0x75, 0x36, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x75, 0x33, 0x3a, 0x6f, 0x72,
172 +
                0x63, 0x75, 0x31, 0x34, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x64,
173 +
                0x64, 0x72, 0x65, 0x73, 0x73, 0x32, 0x30, 0x3a, 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d,
174 +
                0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb, 0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc,
175 +
                0x75, 0x36, 0x3a, 0x77, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x75, 0x34, 0x3a, 0x77, 0x61,
176 +
                0x6e, 0x64, 0x65, 0x65, 0x64, 0x75, 0x37, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69,
177 +
                0x64, 0x75, 0x35, 0x3a, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x75, 0x36, 0x3a, 0x76, 0x61,
178 +
                0x6c, 0x75, 0x65, 0x73, 0x64, 0x75, 0x37, 0x3a, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69,
179 +
                0x64, 0x69, 0x31, 0x30, 0x65, 0x65, 0x65, 0x65, 0x31, 0x3a, 0x67, 0x33, 0x32, 0x3a,
180 +
                0xd6, 0x93, 0xda, 0x38, 0x66, 0xa3, 0x4d, 0x65, 0x9e, 0x01, 0x4f, 0x97, 0xc8, 0xfe,
181 +
                0xb0, 0x8a, 0xfe, 0x2e, 0x97, 0xc9, 0x9e, 0x3f, 0x33, 0x89, 0xda, 0x02, 0x5f, 0xd0,
182 +
                0x66, 0x5c, 0x62, 0x1c, 0x31, 0x3a, 0x6e, 0x69, 0x30, 0x65, 0x31, 0x3a, 0x70, 0x36,
183 +
                0x35, 0x3a, 0x04, 0x46, 0x11, 0x5b, 0x01, 0x31, 0xba, 0xcc, 0xf9, 0x4a, 0x58, 0x56,
184 +
                0xed, 0xe8, 0x71, 0x29, 0x5f, 0x6f, 0x3d, 0x35, 0x2e, 0x68, 0x47, 0xcd, 0xa9, 0xc0,
185 +
                0x3e, 0x89, 0xfe, 0x09, 0xf7, 0x32, 0x80, 0x87, 0x11, 0xec, 0x97, 0xaf, 0x6e, 0x34,
186 +
                0x1f, 0x11, 0x0a, 0x32, 0x6d, 0xa1, 0xbd, 0xb8, 0x1f, 0x5a, 0xe3, 0xba, 0xdf, 0x76,
187 +
                0xa9, 0x0b, 0x22, 0xc8, 0xc4, 0x91, 0xae, 0xd3, 0xaa, 0xa2, 0x96, 0x31, 0x3a, 0x73,
188 +
                0x32, 0x30, 0x3a, 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d, 0x66, 0x2a, 0x4a, 0x9b, 0xfc,
189 +
                0xf9, 0xcb, 0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc, 0x31, 0x3a, 0x74, 0x75, 0x32,
190 +
                0x37, 0x3a, 0x32, 0x30, 0x31, 0x38, 0x2d, 0x31, 0x31, 0x2d, 0x32, 0x31, 0x54, 0x30,
191 +
                0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
192 +
                0x5a, 0x31, 0x3a, 0x75, 0x6c, 0x32, 0x30, 0x3a, 0xc2, 0xa8, 0x60, 0x14, 0x07, 0x3d,
193 +
                0x66, 0x2a, 0x4a, 0x9b, 0xfc, 0xf9, 0xcb, 0x54, 0x26, 0x3d, 0xfa, 0x4f, 0x5c, 0xbc,
194 +
                0x65, 0x65, 0x65, 0x65,
195 195
            };
196 196
197 197
            Block<PolymorphicAction<BaseAction>> actual =
@@ -338,19 +338,22 @@
Loading
338 338
339 339
            Transaction<DumbAction>[] blockIdx2Txs =
340 340
            {
341 +
                // Note that these timestamps in themselves does not have any meanings but are
342 +
                // only arbitrary.  These purpose to make their evaluation order in a block
343 +
                // equal to the order we (the test) intend:
341 344
                Transaction<DumbAction>.Create(
342 345
                    0,
343 346
                    _fx.TxFixture.PrivateKey1,
344 347
                    _fx.Genesis.Hash,
345 348
                    new[] { MakeAction(addresses[0], 'D') },
346 -
                    timestamp: DateTimeOffset.MinValue.AddSeconds(3)
349 +
                    timestamp: DateTimeOffset.MinValue.AddSeconds(1)
347 350
                ),
348 351
                Transaction<DumbAction>.Create(
349 352
                    0,
350 353
                    _fx.TxFixture.PrivateKey2,
351 354
                    _fx.Genesis.Hash,
352 355
                    new[] { MakeAction(addresses[3], 'E') },
353 -
                    timestamp: DateTimeOffset.MinValue.AddSeconds(4)
356 +
                    timestamp: DateTimeOffset.MinValue.AddSeconds(3)
354 357
                ),
355 358
                Transaction<DumbAction>.Create(
356 359
                    0,
@@ -368,7 +371,7 @@
Loading
368 371
                            recordRandom: true
369 372
                        ),
370 373
                    },
371 -
                    timestamp: DateTimeOffset.MinValue.AddSeconds(6)
374 +
                    timestamp: DateTimeOffset.MinValue.AddSeconds(5)
372 375
                ),
373 376
            };
374 377
            i = 0;
@@ -528,7 +531,10 @@
Loading
528 531
                miner: _fx.Next.Miner,
529 532
                previousHash: _fx.Next.PreviousHash,
530 533
                timestamp: _fx.Next.Timestamp,
531 -
                transactions: _fx.Next.Transactions);
534 +
                transactions: _fx.Next.Transactions,
535 +
                preEvaluationHash: _fx.Next.PreEvaluationHash,
536 +
                protocolVersion: _fx.Next.ProtocolVersion
537 +
            );
532 538
533 539
            Assert.Throws<InvalidBlockNonceException>(() =>
534 540
                invalidBlock.Validate(DateTimeOffset.UtcNow));
@@ -629,7 +635,7 @@
Loading
629 635
        }
630 636
631 637
        [Fact]
632 -
        public void CanConvertToRaw()
638 +
        public void ConvertToRaw()
633 639
        {
634 640
            RawBlock rawGenesis = _fx.Genesis.ToRawBlock();
635 641
            Assert.Equal(0, rawGenesis.Header.Index);
@@ -649,9 +655,9 @@
Loading
649 655
            AssertBytesEqual(
650 656
                new byte[32]
651 657
                {
652 -
                    0xd4, 0xf3, 0x58, 0x34, 0xe2, 0x7d, 0x5a, 0xb4, 0x59, 0xa4, 0xd4, 0x01, 0xe9,
653 -
                    0xa0, 0x82, 0x68, 0xe3, 0xfe, 0x32, 0x1b, 0x8c, 0x68, 0x50, 0x75, 0xae, 0xc5,
654 -
                    0xbd, 0x5d, 0x18, 0xd6, 0x42, 0xaa,
658 +
                    0xd6, 0x93, 0xda, 0x38, 0x66, 0xa3, 0x4d, 0x65, 0x9e, 0x01, 0x4f, 0x97,
659 +
                    0xc8, 0xfe, 0xb0, 0x8a, 0xfe, 0x2e, 0x97, 0xc9, 0x9e, 0x3f, 0x33, 0x89,
660 +
                    0xda, 0x02, 0x5f, 0xd0, 0x66, 0x5c, 0x62, 0x1c,
655 661
                },
656 662
                rawGenesis.Header.Hash.ToArray()
657 663
            );
@@ -669,11 +675,11 @@
Loading
669 675
            Assert.Empty(rawNext.Transactions);
670 676
            Assert.Empty(rawNext.Header.TxHash);
671 677
            AssertBytesEqual(
672 -
                new byte[32]
678 +
                new byte[]
673 679
                {
674 -
                    0x1c, 0xd4, 0x45, 0x16, 0x24, 0xef, 0x9c, 0x79, 0xe2, 0xc2, 0xfb, 0x5a, 0x8e,
675 -
                    0x79, 0x1e, 0x4f, 0xa5, 0x6a, 0x7d, 0x8c, 0x61, 0x0c, 0x14, 0xa8, 0xa3, 0x4a,
676 -
                    0xe1, 0x75, 0xb5, 0x20, 0x5c, 0xf7,
680 +
                    0x1b, 0xba, 0x9f, 0xcf, 0x4c, 0x81, 0x52, 0xc8, 0x99, 0xed, 0x16, 0x74, 0xec,
681 +
                    0xbf, 0x4a, 0x65, 0x71, 0xc2, 0x71, 0x92, 0x2c, 0x08, 0x84, 0xae, 0x80, 0x9f,
682 +
                    0x91, 0xf0, 0x37, 0xbe, 0xd8, 0xfc,
677 683
                },
678 684
                rawNext.Header.Hash.ToArray()
679 685
            );
@@ -690,20 +696,20 @@
Loading
690 696
            );
691 697
            Assert.Single(rawHasText.Transactions);
692 698
            AssertBytesEqual(
693 -
                new byte[32]
699 +
                new byte[]
694 700
                {
695 -
                    0x97, 0x3c, 0xa5, 0xff, 0xce, 0x89, 0x84, 0xc0, 0x39, 0xe6, 0x3f, 0x2b, 0xee,
696 -
                    0x2f, 0x01, 0x67, 0x00, 0x51, 0x00, 0xa0, 0xfb, 0xa4, 0x71, 0x4f, 0xf6, 0xfb,
697 -
                    0x67, 0xe0, 0xaf, 0x61, 0x53, 0x00,
701 +
                    0x75, 0xa9, 0x2b, 0xf5, 0xcf, 0xf3, 0x5d, 0x5b, 0x4d, 0x0c, 0xb8, 0x40, 0xab,
702 +
                    0x61, 0x11, 0x3d, 0xee, 0xab, 0x6f, 0x78, 0x0f, 0x9b, 0x69, 0xfc, 0x48, 0x68,
703 +
                    0xe6, 0xba, 0xac, 0xdc, 0xcb, 0x54,
698 704
                },
699 705
                rawHasText.Header.TxHash.ToArray()
700 706
            );
701 707
            AssertBytesEqual(
702 708
                new byte[32]
703 709
                {
704 -
                    0x22, 0xe1, 0x3b, 0xb5, 0x46, 0x65, 0x91, 0x34, 0x28, 0xbc, 0x55, 0x86, 0xce,
705 -
                    0xbd, 0x50, 0xdc, 0x25, 0x50, 0xb0, 0x36, 0xa6, 0x18, 0xe9, 0xb0, 0x5d, 0xb1,
706 -
                    0x1c, 0x76, 0xad, 0xb0, 0x0a, 0xd7,
710 +
                    0x28, 0x40, 0xe8, 0x6a, 0x27, 0xda, 0x6e, 0x0d, 0xe6, 0x2e, 0xab, 0xab, 0x32,
711 +
                    0x95, 0x3e, 0x3b, 0x33, 0xf2, 0xa6, 0x51, 0x18, 0x35, 0xf1, 0x85, 0x1a, 0x1a,
712 +
                    0xcc, 0x51, 0xd7, 0x71, 0xcb, 0x99,
707 713
                },
708 714
                rawHasText.Header.Hash.ToArray()
709 715
            );
@@ -740,19 +746,19 @@
Loading
740 746
            var codec = new Codec();
741 747
            // Case of a block with no any txs, contained state root.
742 748
            // Size of RawBlock
743 -
            Assert.Equal(208, emptyBlock.Serialize().Length);
749 +
            Assert.Equal(214, emptyBlock.Serialize().Length);
744 750
            // Size of BlockDigest
745 -
            Assert.Equal(208, emptyBlock.ToBlockDigest().Serialize().Length);
751 +
            Assert.Equal(214, emptyBlock.ToBlockDigest().Serialize().Length);
746 752
            // Size of BlockHeader
747 -
            Assert.Equal(203, codec.Encode(emptyBlock.Header.ToBencodex()).Length);
753 +
            Assert.Equal(209, codec.Encode(emptyBlock.Header.ToBencodex()).Length);
748 754
749 755
            // Case of a block with txs, not contained state root.
750 756
            // Size of RawBlock
751 -
            Assert.Equal(697, txBlock.Serialize().Length);
757 +
            Assert.Equal(704, txBlock.Serialize().Length);
752 758
            // Size of BlockDigest
753 -
            Assert.Equal(293, txBlock.ToBlockDigest().Serialize().Length);
759 +
            Assert.Equal(299, txBlock.ToBlockDigest().Serialize().Length);
754 760
            // Size of BlockHeader
755 -
            Assert.Equal(248, codec.Encode(txBlock.Header.ToBencodex()).Length);
761 +
            Assert.Equal(254, codec.Encode(txBlock.Header.ToBencodex()).Length);
756 762
        }
757 763
758 764
        [Fact]
@@ -786,7 +792,7 @@
Loading
786 792
                timestamp: DateTimeOffset.UtcNow,
787 793
                transactions: new Transaction<DumbAction>[0]
788 794
            );
789 -
            Assert.Equal(140, block.BytesLength);
795 +
            Assert.Equal(146, block.BytesLength);
790 796
        }
791 797
792 798
        [Fact]

@@ -264,5 +264,21 @@
Loading
264 264
                return hashCode;
265 265
            }
266 266
        }
267 +
268 +
        public override string ToString()
269 +
        {
270 +
            const string T = "true", F = "false";
271 +
            string transfer = Transfer is Tuple<Address, Address, BigInteger> t
272 +
                ? $"({t.Item1}, {t.Item2}, {t.Item3})"
273 +
                : "null";
274 +
            return $"{nameof(DumbAction)} {{ " +
275 +
                $"{nameof(TargetAddress)} = {TargetAddress}, " +
276 +
                $"{nameof(Item)} = {Item ?? string.Empty}, " +
277 +
                $"{nameof(RecordRehearsal)} = {(RecordRehearsal ? T : F)}, " +
278 +
                $"{nameof(RecordRandom)} = {(RecordRandom ? T : F)}, " +
279 +
                $"{nameof(Idempotent)} = {(Idempotent ? T : F)}, " +
280 +
                $"{nameof(Transfer)} = {transfer} " +
281 +
                "}";
282 +
        }
267 283
    }
268 284
}

@@ -17,27 +17,29 @@
Loading
17 17
    {
18 18
        internal const string TimestampFormat = "yyyy-MM-ddTHH:mm:ss.ffffffZ";
19 19
20 -
        private static readonly byte[] IndexKey = { 0x69 }; // 'i'
20 +
        internal static readonly byte[] ProtocolVersionKey = { 0x00 };
21 21
22 -
        private static readonly byte[] TimestampKey = { 0x74 }; // 't'
22 +
        internal static readonly byte[] IndexKey = { 0x69 }; // 'i'
23 23
24 -
        private static readonly byte[] DifficultyKey = { 0x64 }; // 'd'
24 +
        internal static readonly byte[] TimestampKey = { 0x74 }; // 't'
25 25
26 -
        private static readonly byte[] TotalDifficultyKey = { 0x54 }; // 'T'
26 +
        internal static readonly byte[] DifficultyKey = { 0x64 }; // 'd'
27 27
28 -
        private static readonly byte[] NonceKey = { 0x6e }; // 'n'
28 +
        internal static readonly byte[] TotalDifficultyKey = { 0x54 }; // 'T'
29 29
30 -
        private static readonly byte[] MinerKey = { 0x6d }; // 'm'
30 +
        internal static readonly byte[] NonceKey = { 0x6e }; // 'n'
31 31
32 -
        private static readonly byte[] PreviousHashKey = { 0x70 }; // 'p'
32 +
        internal static readonly byte[] MinerKey = { 0x6d }; // 'm'
33 33
34 -
        private static readonly byte[] TxHashKey = { 0x78 }; // 'x'
34 +
        internal static readonly byte[] PreviousHashKey = { 0x70 }; // 'p'
35 35
36 -
        private static readonly byte[] HashKey = { 0x68 }; // 'h'
36 +
        internal static readonly byte[] TxHashKey = { 0x78 }; // 'x'
37 37
38 -
        private static readonly byte[] StateRootHashKey = { 0x73 }; // 's'
38 +
        internal static readonly byte[] HashKey = { 0x68 }; // 'h'
39 39
40 -
        private static readonly byte[] PreEvaluationHashKey = { 0x63 }; // 'c'
40 +
        internal static readonly byte[] StateRootHashKey = { 0x73 }; // 's'
41 +
42 +
        internal static readonly byte[] PreEvaluationHashKey = { 0x63 }; // 'c'
41 43
42 44
        private static readonly TimeSpan TimestampThreshold =
43 45
            TimeSpan.FromSeconds(15);
@@ -45,6 +47,8 @@
Loading
45 47
        /// <summary>
46 48
        /// Creates a <see cref="BlockHeader"/> instance.
47 49
        /// </summary>
50 +
        /// <param name="protocolVersion">The protocol version.  Goes to the <see
51 +
        /// cref="ProtocolVersion"/>.</param>
48 52
        /// <param name="index">The height of the block.  Goes to the <see cref="Index"/>.
49 53
        /// </param>
50 54
        /// <param name="timestamp">The time this block is created.
@@ -71,6 +75,7 @@
Loading
71 75
        /// <param name="stateRootHash">The <see cref="ITrie.Hash"/> of the states on the block.
72 76
        /// </param>
73 77
        public BlockHeader(
78 +
            int protocolVersion,
74 79
            long index,
75 80
            string timestamp,
76 81
            ImmutableArray<byte> nonce,
@@ -83,6 +88,7 @@
Loading
83 88
            ImmutableArray<byte> preEvaluationHash,
84 89
            ImmutableArray<byte> stateRootHash)
85 90
        {
91 +
            ProtocolVersion = protocolVersion;
86 92
            Index = index;
87 93
            Timestamp = timestamp;
88 94
            Nonce = nonce;
@@ -98,6 +104,9 @@
Loading
98 104
99 105
        public BlockHeader(Bencodex.Types.Dictionary dict)
100 106
        {
107 +
            ProtocolVersion = dict.ContainsKey(ProtocolVersionKey)
108 +
                ? (int)dict.GetValue<Integer>(ProtocolVersionKey)
109 +
                : 0;
101 110
            Index = dict.GetValue<Integer>(IndexKey);
102 111
            Timestamp = dict.GetValue<Text>(TimestampKey);
103 112
            Difficulty = dict.GetValue<Integer>(DifficultyKey);
@@ -129,6 +138,11 @@
Loading
129 138
                : ImmutableArray<byte>.Empty;
130 139
        }
131 140
141 +
        /// <summary>
142 +
        /// The protocol version number.
143 +
        /// </summary>
144 +
        public int ProtocolVersion { get; }
145 +
132 146
        public long Index { get; }
133 147
134 148
        public string Timestamp { get; }
@@ -196,6 +210,11 @@
Loading
196 210
                .Add(NonceKey, Nonce.ToArray())
197 211
                .Add(HashKey, Hash.ToArray());
198 212
213 +
            if (ProtocolVersion != 0)
214 +
            {
215 +
                dict = dict.Add(ProtocolVersionKey, ProtocolVersion);
216 +
            }
217 +
199 218
            if (Miner.Any())
200 219
            {
201 220
                dict = dict.Add(MinerKey, Miner.ToArray());
@@ -226,6 +245,14 @@
Loading
226 245
227 246
        internal void Validate(DateTimeOffset currentTime)
228 247
        {
248 +
            if (ProtocolVersion < 0)
249 +
            {
250 +
                throw new InvalidBlockProtocolVersionException(
251 +
                    ProtocolVersion,
252 +
                    $"A block's protocol version cannot be less than zero: {ProtocolVersion}."
253 +
                );
254 +
            }
255 +
229 256
            DateTimeOffset ts = DateTimeOffset.ParseExact(
230 257
                Timestamp,
231 258
                TimestampFormat,

@@ -21,6 +21,11 @@
Loading
21 21
    public class Block<T>
22 22
        where T : IAction, new()
23 23
    {
24 +
        /// <summary>
25 +
        /// The most latest protocol version.
26 +
        /// </summary>
27 +
        public const int CurrentProtocolVersion = 1;
28 +
24 29
        private int _bytesLength;
25 30
26 31
        /// <summary>
@@ -51,6 +56,8 @@
Loading
51 56
        /// Automatically determined if <c>null</c> is passed (which is default).</param>
52 57
        /// <param name="stateRootHash">The <see cref="ITrie.Hash"/> of the states on the block.
53 58
        /// </param>
59 +
        /// <param name="protocolVersion">The protocol version. <see cref="CurrentProtocolVersion"/>
60 +
        /// by default.</param>
54 61
        /// <seealso cref="Mine"/>
55 62
        public Block(
56 63
            long index,
@@ -62,8 +69,10 @@
Loading
62 69
            DateTimeOffset timestamp,
63 70
            IEnumerable<Transaction<T>> transactions,
64 71
            HashDigest<SHA256>? preEvaluationHash = null,
65 -
            HashDigest<SHA256>? stateRootHash = null)
72 +
            HashDigest<SHA256>? stateRootHash = null,
73 +
            int protocolVersion = CurrentProtocolVersion)
66 74
        {
75 +
            ProtocolVersion = protocolVersion;
67 76
            Index = index;
68 77
            Difficulty = difficulty;
69 78
            TotalDifficulty = totalDifficulty;
@@ -126,9 +135,12 @@
Loading
126 135
        {
127 136
        }
128 137
138 +
        // FIXME: Should this necessarily be a public constructor?
139 +
        // See also <https://github.com/planetarium/libplanet/issues/1146>.
129 140
        public Block(
130 141
            Block<T> block,
131 -
            HashDigest<SHA256>? stateRootHash)
142 +
            HashDigest<SHA256>? stateRootHash
143 +
        )
132 144
            : this(
133 145
                block.Index,
134 146
                block.Difficulty,
@@ -139,39 +151,47 @@
Loading
139 151
                block.Timestamp,
140 152
                block.Transactions,
141 153
                block.PreEvaluationHash,
142 -
                stateRootHash)
154 +
                stateRootHash,
155 +
                protocolVersion: block.ProtocolVersion
156 +
            )
143 157
        {
144 158
        }
145 159
146 160
        private Block(RawBlock rb)
147 161
            : this(
162 +
#pragma warning disable SA1118
163 +
                rb.Header.ProtocolVersion,
148 164
                new HashDigest<SHA256>(rb.Header.Hash),
149 165
                rb.Header.Index,
150 166
                rb.Header.Difficulty,
151 167
                rb.Header.TotalDifficulty,
152 168
                new Nonce(rb.Header.Nonce.ToArray()),
153 169
                rb.Header.Miner.Any() ? new Address(rb.Header.Miner) : (Address?)null,
154 -
#pragma warning disable MEN002 // Line is too long
155 -
                rb.Header.PreviousHash.Any() ? new HashDigest<SHA256>(rb.Header.PreviousHash) : (HashDigest<SHA256>?)null,
156 -
#pragma warning restore MEN002 // Line is too long
170 +
                rb.Header.PreviousHash.Any()
171 +
                    ? new HashDigest<SHA256>(rb.Header.PreviousHash)
172 +
                    : (HashDigest<SHA256>?)null,
157 173
                DateTimeOffset.ParseExact(
158 174
                    rb.Header.Timestamp,
159 175
                    BlockHeader.TimestampFormat,
160 176
                    CultureInfo.InvariantCulture).ToUniversalTime(),
161 -
#pragma warning disable MEN002 // Line is too long
162 -
                rb.Header.TxHash.Any() ? new HashDigest<SHA256>(rb.Header.TxHash) : (HashDigest<SHA256>?)null,
163 -
#pragma warning restore MEN002 // Line is too long
177 +
                rb.Header.TxHash.Any()
178 +
                    ? new HashDigest<SHA256>(rb.Header.TxHash)
179 +
                    : (HashDigest<SHA256>?)null,
164 180
                rb.Transactions
165 181
                    .Select(tx => Transaction<T>.Deserialize(tx.ToArray()))
166 182
                    .ToList(),
167 -
#pragma warning disable MEN002 // Line is too long
168 -
                rb.Header.PreEvaluationHash.Any() ? new HashDigest<SHA256>(rb.Header.PreEvaluationHash) : (HashDigest<SHA256>?)null,
169 -
                rb.Header.StateRootHash.Any() ? new HashDigest<SHA256>(rb.Header.StateRootHash) : (HashDigest<SHA256>?)null)
170 -
#pragma warning restore MEN002 // Line is too long
183 +
                rb.Header.PreEvaluationHash.Any()
184 +
                    ? new HashDigest<SHA256>(rb.Header.PreEvaluationHash)
185 +
                    : (HashDigest<SHA256>?)null,
186 +
                rb.Header.StateRootHash.Any()
187 +
                    ? new HashDigest<SHA256>(rb.Header.StateRootHash)
188 +
                    : (HashDigest<SHA256>?)null)
189 +
#pragma warning restore SA1118
171 190
        {
172 191
        }
173 192
174 193
        private Block(
194 +
            int protocolVersion,
175 195
            HashDigest<SHA256> hash,
176 196
            long index,
177 197
            long difficulty,
@@ -186,6 +206,7 @@
Loading
186 206
            HashDigest<SHA256>? stateRootHash
187 207
        )
188 208
        {
209 +
            ProtocolVersion = protocolVersion;
189 210
            Index = index;
190 211
            Difficulty = difficulty;
191 212
            TotalDifficulty = totalDifficulty;
@@ -205,6 +226,12 @@
Loading
205 226
            Transactions = transactions.ToImmutableArray();
206 227
        }
207 228
229 +
        /// <summary>
230 +
        /// The protocol version number.
231 +
        /// </summary>
232 +
        [IgnoreDuringEquals]
233 +
        public int ProtocolVersion { get; }
234 +
208 235
        /// <summary>
209 236
        /// <see cref="Hash"/> is derived from a serialized <see cref="Block{T}"/>
210 237
        /// after <see cref="Transaction{T}.Actions"/> are evaluated.
@@ -288,6 +315,7 @@
Loading
288 315
289 316
                // FIXME: When hash is not assigned, should throw an exception.
290 317
                return new BlockHeader(
318 +
                    protocolVersion: ProtocolVersion,
291 319
                    index: Index,
292 320
                    timestamp: timestampAsString,
293 321
                    nonce: Nonce.ToByteArray().ToImmutableArray(),
@@ -324,6 +352,7 @@
Loading
324 352
        /// <param name="timestamp">The <see cref="DateTimeOffset"/> when mining started.</param>
325 353
        /// <param name="transactions"><see cref="Transaction{T}"/>s that are going to be included
326 354
        /// in the block.</param>
355 +
        /// <param name="protocolVersion">The protocol version.</param>
327 356
        /// <param name="cancellationToken">
328 357
        /// A cancellation token used to propagate notification that this
329 358
        /// operation should be canceled.</param>
@@ -336,6 +365,7 @@
Loading
336 365
            HashDigest<SHA256>? previousHash,
337 366
            DateTimeOffset timestamp,
338 367
            IEnumerable<Transaction<T>> transactions,
368 +
            int protocolVersion = CurrentProtocolVersion,
339 369
            CancellationToken cancellationToken = default(CancellationToken))
340 370
        {
341 371
            var txs = transactions.OrderBy(tx => tx.Id).ToImmutableArray();
@@ -347,7 +377,8 @@
Loading
347 377
                miner,
348 378
                previousHash,
349 379
                timestamp,
350 -
                txs);
380 +
                txs,
381 +
                protocolVersion: protocolVersion);
351 382
352 383
            // Poor man' way to optimize stamp...
353 384
            // FIXME: We need to rather reorganize the serialization layout.
@@ -658,6 +689,11 @@
Loading
658 689
                .Add("difficulty", Difficulty)
659 690
                .Add("nonce", Nonce.ToByteArray());
660 691
692 +
            if (ProtocolVersion != 0)
693 +
            {
694 +
                dict = dict.Add("protocol_version", ProtocolVersion);
695 +
            }
696 +
661 697
            if (!(Miner is null))
662 698
            {
663 699
                dict = dict.Add("reward_beneficiary", Miner.Value.ToByteArray());
Files Coverage
Libplanet 84.84%
Libplanet.Analyzers.Tests 84.07%
Libplanet.RocksDBStore 92.12%
Libplanet.RocksDBStore.Tests 88.00%
Libplanet.Stun.Tests/Stun 99.56%
Libplanet.Stun/Stun 78.44%
Libplanet.Tests 89.27%
Libplanet.Analyzers/ActionAnalyzer.cs 82.58%
Project Totals (347 files) 87.41%
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