1
using System;
2
using System.Collections.Generic;
3
using System.Collections.Immutable;
4
using System.Diagnostics.Contracts;
5
using System.Globalization;
6
using System.Linq;
7
using System.Numerics;
8
using System.Security.Cryptography;
9
using System.Text;
10
using System.Threading;
11
using Bencodex;
12
using Bencodex.Types;
13
using Libplanet.Action;
14
using Libplanet.Assets;
15
using Libplanet.Store.Trie;
16
using Libplanet.Tx;
17

18
namespace Libplanet.Blocks
19
{
20
    [Equals]
21
    public class Block<T>
22
        where T : IAction, new()
23
    {
24
        /// <summary>
25
        /// Creates a <see cref="Block{T}"/> instance by manually filling all field values.
26
        /// For a more automated way, see also <see cref="Mine"/> method.
27
        /// </summary>
28
        /// <param name="index">The height of the block to create.  Goes to the <see cref="Index"/>.
29
        /// </param>
30
        /// <param name="difficulty">The mining difficulty that <paramref name="nonce"/> has to
31
        /// satisfy.  Goes to the <see cref="Difficulty"/>.</param>
32
        /// <param name="totalDifficulty">The total mining difficulty until this block.
33
        /// See also <see cref="Difficulty"/>.</param>
34
        /// <param name="nonce">The nonce which satisfy the given <paramref name="difficulty"/> with
35
        /// any other field values.  Goes to the <see cref="Nonce"/>.</param>
36
        /// <param name="miner">An optional address refers to who mines this block.
37
        /// Goes to the <see cref="Miner"/>.</param>
38
        /// <param name="previousHash">The previous block's <see cref="Hash"/>.  If it's a genesis
39
        /// block (i.e., <paramref name="index"/> is 0) this should be <c>null</c>.
40
        /// Goes to the <see cref="PreviousHash"/>.</param>
41
        /// <param name="timestamp">The time this block is created.  Goes to
42
        /// the <see cref="Timestamp"/>.</param>
43
        /// <param name="transactions">The transactions to be mined together with this block.
44
        /// Transactions become sorted in an unpredicted-before-mined order and then go to
45
        /// the <see cref="Transactions"/> property.
46
        /// </param>
47
        /// <param name="preEvaluationHash">The hash derived from the block <em>except of</em>
48
        /// <paramref name="stateRootHash"/> (i.e., without action evaluation).
49
        /// Automatically determined if <c>null</c> is passed (which is default).</param>
50
        /// <param name="stateRootHash">The <see cref="ITrie.Hash"/> of the states on the block.
51
        /// </param>
52
        /// <seealso cref="Mine"/>
53 2
        public Block(
54 2
            long index,
55 2
            long difficulty,
56 2
            BigInteger totalDifficulty,
57 2
            Nonce nonce,
58 2
            Address? miner,
59 2
            HashDigest<SHA256>? previousHash,
60 2
            DateTimeOffset timestamp,
61 2
            IEnumerable<Transaction<T>> transactions,
62 2
            HashDigest<SHA256>? preEvaluationHash = null,
63 2
            HashDigest<SHA256>? stateRootHash = null)
64 2
        {
65 2
            Index = index;
66 2
            Difficulty = difficulty;
67 2
            TotalDifficulty = totalDifficulty;
68 2
            Nonce = nonce;
69 2
            Miner = miner;
70 2
            PreviousHash = previousHash;
71 2
            Timestamp = timestamp;
72 2
            Transactions = transactions.OrderBy(tx => tx.Id).ToArray();
73 2
            var codec = new Codec();
74 2
            TxHash = Transactions.Any()
75 2
                ? Hashcash.Hash(
76 2
                    codec.Encode(
77 2
                        new Bencodex.Types.List(Transactions.Select(tx =>
78 2
                            (IValue)tx.ToBencodex(true)))))
79 2
                : (HashDigest<SHA256>?)null;
80 2
            PreEvaluationHash = preEvaluationHash ?? Hashcash.Hash(SerializeForHash());
81 2
            StateRootHash = stateRootHash;
82

83
            // FIXME: This does not need to be computed every time?
84 2
            Hash = Hashcash.Hash(SerializeForHash(stateRootHash));
85

86
            // As the order of transactions should be unpredictable until a block is mined,
87
            // the sorter key should be derived from both a block hash and a txid.
88 2
            var hashInteger = new BigInteger(PreEvaluationHash.ToByteArray());
89

90
            // If there are multiple transactions for the same signer these should be ordered by
91
            // their tx nonces.  So transactions of the same signer should have the same sort key.
92
            // The following logic "flattens" multiple tx ids having the same signer into a single
93
            // txid by applying XOR between them.
94 2
            IImmutableDictionary<Address, IImmutableSet<Transaction<T>>> signerTxs = Transactions
95 2
                .GroupBy(tx => tx.Signer)
96 2
                .ToImmutableDictionary(
97 2
                    g => g.Key,
98 2
                    g => (IImmutableSet<Transaction<T>>)g.ToImmutableHashSet()
99 2
                );
100 2
            IImmutableDictionary<Address, BigInteger> signerTxIds = signerTxs
101 2
                .ToImmutableDictionary(
102 2
                    pair => pair.Key,
103 2
                    pair => pair.Value
104 2
                        .Select(tx => new BigInteger(tx.Id.ToByteArray()))
105 2
                        .OrderBy(txid => txid)
106 1
                        .Aggregate((a, b) => a ^ b)
107 2
                );
108

109
            // Order signers by values derivied from both block hash and their "flatten" txid:
110 2
            IImmutableList<Address> signers = signerTxIds
111 2
                .OrderBy(pair => pair.Value ^ hashInteger)
112 2
                .Select(pair => pair.Key)
113 2
                .ToImmutableArray();
114

115
            // Order transactions for each signer by their tx nonces:
116 2
            Transactions = signers
117 2
                .SelectMany(signer => signerTxs[signer].OrderBy(tx => tx.Nonce))
118 2
                .ToImmutableArray();
119 2
        }
120

121
        /// <summary>
122
        /// Creates a <see cref="Block{T}"/> instance from its serialization.
123
        /// </summary>
124
        /// <param name="dict">The <see cref="Bencodex.Types.Dictionary"/>
125
        /// representation of <see cref="Block{T}"/> instance.
126
        /// </param>
127
        public Block(Bencodex.Types.Dictionary dict)
128 1
            : this(new RawBlock(dict))
129 1
        {
130 1
        }
131

132
        public Block(
133
            Block<T> block,
134
            HashDigest<SHA256>? stateRootHash)
135 1
            : this(
136 1
                block.Index,
137 1
                block.Difficulty,
138 1
                block.TotalDifficulty,
139 1
                block.Nonce,
140 1
                block.Miner,
141 1
                block.PreviousHash,
142 1
                block.Timestamp,
143 1
                block.Transactions,
144 1
                block.PreEvaluationHash,
145 1
                stateRootHash)
146 1
        {
147 1
        }
148

149
        private Block(RawBlock rb)
150 1
            : this(
151 1
                rb.Header.Index,
152 1
                rb.Header.Difficulty,
153 1
                rb.Header.TotalDifficulty,
154 1
                new Nonce(rb.Header.Nonce.ToArray()),
155 1
                rb.Header.Miner.Any() ? new Address(rb.Header.Miner) : (Address?)null,
156 1
#pragma warning disable MEN002 // Line is too long
157 1
                rb.Header.PreviousHash.Any() ? new HashDigest<SHA256>(rb.Header.PreviousHash) : (HashDigest<SHA256>?)null,
158 1
#pragma warning restore MEN002 // Line is too long
159 1
                DateTimeOffset.ParseExact(
160 1
                    rb.Header.Timestamp,
161 1
                    BlockHeader.TimestampFormat,
162 1
                    CultureInfo.InvariantCulture).ToUniversalTime(),
163 1
                rb.Transactions
164 1
                    .Select(tx => Transaction<T>.Deserialize(tx.ToArray()))
165 1
                    .ToList(),
166 1
#pragma warning disable MEN002 // Line is too long
167 1
                rb.Header.PreEvaluationHash.Any() ? new HashDigest<SHA256>(rb.Header.PreEvaluationHash) : (HashDigest<SHA256>?)null,
168 1
                rb.Header.StateRootHash.Any() ? new HashDigest<SHA256>(rb.Header.StateRootHash) : (HashDigest<SHA256>?)null)
169
#pragma warning restore MEN002 // Line is too long
170 1
        {
171 1
        }
172

173
        /// <summary>
174
        /// <see cref="Hash"/> is derived from a serialized <see cref="Block{T}"/>
175
        /// after <see cref="Transaction{T}.Actions"/> are evaluated.
176
        /// </summary>
177
        /// <seealso cref="PreEvaluationHash"/>
178
        /// <seealso cref="StateRootHash"/>
179 2
        public HashDigest<SHA256> Hash { get; }
180

181
        /// <summary>
182
        /// The hash derived from the block <em>except of</em>
183
        /// <see cref="StateRootHash"/> (i.e., without action evaluation).
184
        /// Used for <see cref="BlockHeader.Validate"/> checking <see cref="Nonce"/>.
185
        /// </summary>
186
        /// <seealso cref="Nonce"/>
187
        /// <seealso cref="BlockHeader.Validate"/>
188 2
        public HashDigest<SHA256> PreEvaluationHash { get; }
189

190
        /// <summary>
191
        /// The <see cref="ITrie.Hash"/> of the states on the block.
192
        /// </summary>
193
        /// <seealso cref="ITrie.Hash"/>
194 2
        public HashDigest<SHA256>? StateRootHash { get; }
195

196
        [IgnoreDuringEquals]
197 2
        public long Index { get; }
198

199
        [IgnoreDuringEquals]
200 2
        public long Difficulty { get; }
201

202
        [IgnoreDuringEquals]
203 2
        public BigInteger TotalDifficulty { get; }
204

205
        [IgnoreDuringEquals]
206 2
        public Nonce Nonce { get; }
207

208
        [IgnoreDuringEquals]
209 2
        public Address? Miner { get; }
210

211
        [IgnoreDuringEquals]
212 2
        public HashDigest<SHA256>? PreviousHash { get; }
213

214
        [IgnoreDuringEquals]
215 2
        public DateTimeOffset Timestamp { get; }
216

217
        [IgnoreDuringEquals]
218 2
        public HashDigest<SHA256>? TxHash { get; }
219

220
        [IgnoreDuringEquals]
221 2
        public IEnumerable<Transaction<T>> Transactions { get; }
222

223
        public static bool operator ==(Block<T> left, Block<T> right) =>
224
            Operator.Weave(left, right);
225

226
        public static bool operator !=(Block<T> left, Block<T> right) =>
227
            Operator.Weave(left, right);
228

229
        /// <summary>
230
        /// Generate a block with given <paramref name="transactions"/>.
231
        /// </summary>
232
        /// <param name="index">Index of the block.</param>
233
        /// <param name="difficulty">Difficulty to find the <see cref="Block{T}"/>
234
        /// <see cref="Nonce"/>.</param>
235
        /// <param name="previousTotalDifficulty">The total difficulty until the previous
236
        /// <see cref="Block{T}"/>.</param>
237
        /// <param name="miner">The <see cref="Address"/> of miner that mined the block.</param>
238
        /// <param name="previousHash">
239
        /// The <see cref="HashDigest{SHA256}"/> of previous block.
240
        /// </param>
241
        /// <param name="timestamp">The <see cref="DateTimeOffset"/> when mining started.</param>
242
        /// <param name="transactions"><see cref="Transaction{T}"/>s that are going to be included
243
        /// in the block.</param>
244
        /// <param name="cancellationToken">
245
        /// A cancellation token used to propagate notification that this
246
        /// operation should be canceled.</param>
247
        /// <returns>A <see cref="Block{T}"/> that mined.</returns>
248
        public static Block<T> Mine(
249
            long index,
250
            long difficulty,
251
            BigInteger previousTotalDifficulty,
252
            Address miner,
253
            HashDigest<SHA256>? previousHash,
254
            DateTimeOffset timestamp,
255
            IEnumerable<Transaction<T>> transactions,
256
            CancellationToken cancellationToken = default(CancellationToken))
257 2
        {
258 2
            var txs = transactions.OrderBy(tx => tx.Id).ToImmutableArray();
259 2
            Block<T> MakeBlock(Nonce n) => new Block<T>(
260 2
                index,
261 2
                difficulty,
262 2
                previousTotalDifficulty + difficulty,
263 2
                n,
264 2
                miner,
265 2
                previousHash,
266 2
                timestamp,
267 2
                txs);
268

269
            // Poor man' way to optimize stamp...
270
            // FIXME: We need to rather reorganize the serialization layout.
271 2
            byte[] emptyNonce = MakeBlock(new Nonce(new byte[0])).SerializeForHash();
272 2
            byte[] oneByteNonce = MakeBlock(new Nonce(new byte[1])).SerializeForHash();
273 2
            int offset = 0;
274 2
            while (offset < emptyNonce.Length && emptyNonce[offset].Equals(oneByteNonce[offset]))
275 2
            {
276 2
                offset++;
277 2
            }
278

279
            const int nonceLength = 2;  // In Bencodex, empty bytes are represented as "0:".
280 2
            byte[] stampPrefix = new byte[offset];
281 2
            Array.Copy(emptyNonce, stampPrefix, stampPrefix.Length);
282 2
            byte[] stampSuffix = new byte[emptyNonce.Length - offset - nonceLength];
283 2
            Array.Copy(emptyNonce, offset + nonceLength, stampSuffix, 0, stampSuffix.Length);
284

285 2
            Nonce nonce = Hashcash.Answer(
286 2
                n =>
287 2
                {
288 2
                    int nLen = n.ByteArray.Length;
289 2
                    byte[] nLenStr = Encoding.ASCII.GetBytes(
290 2
                        nLen.ToString(CultureInfo.InvariantCulture));
291 2
                    int totalLen =
292 2
                        stampPrefix.Length + nLenStr.Length + 1 + nLen + stampSuffix.Length;
293 2
                    byte[] stamp = new byte[totalLen];
294 2
                    Array.Copy(stampPrefix, stamp, stampPrefix.Length);
295 2
                    int pos = stampPrefix.Length;
296 2
                    Array.Copy(nLenStr, 0, stamp, pos, nLenStr.Length);
297 2
                    pos += nLenStr.Length;
298 2
                    stamp[pos] = 0x3a;  // ':'
299 2
                    pos++;
300 2
                    n.ByteArray.CopyTo(stamp, pos);
301 2
                    pos += nLen;
302 2
                    Array.Copy(stampSuffix, 0, stamp, pos, stampSuffix.Length);
303 2
                    return stamp;
304 2
                },
305 2
                difficulty,
306 2
                cancellationToken
307 2
            );
308

309 2
            return MakeBlock(nonce);
310 2
        }
311

312
        /// <summary>
313
        /// Decodes a <see cref="Block{T}"/>'s
314
        /// <a href="https://bencodex.org/">Bencodex</a> representation.
315
        /// </summary>
316
        /// <param name="bytes">A <a href="https://bencodex.org/">Bencodex</a>
317
        /// representation of a <see cref="Block{T}"/>.</param>
318
        /// <returns>A decoded <see cref="Block{T}"/> object.</returns>
319
        /// <seealso cref="Serialize()"/>
320
        public static Block<T> Deserialize(byte[] bytes)
321 1
        {
322 1
            IValue value = new Codec().Decode(bytes);
323 1
            if (!(value is Bencodex.Types.Dictionary dict))
324 0
            {
325 0
                throw new DecodingException(
326 0
                    $"Expected {typeof(Bencodex.Types.Dictionary)} but " +
327 0
                    $"{value.GetType()}");
328
            }
329

330 1
            return new Block<T>(dict);
331 1
        }
332

333
        public byte[] Serialize()
334 1
        {
335 1
            var codec = new Codec();
336 1
            return codec.Encode(ToBencodex());
337 1
        }
338

339 1
        public Bencodex.Types.Dictionary ToBencodex() => ToRawBlock().ToBencodex();
340

341
        /// <summary>
342
        /// Executes every <see cref="IAction"/> in the
343
        /// <see cref="Transactions"/> step by step, and emits a pair of
344
        /// a transaction, and an <see cref="ActionEvaluation"/>
345
        /// for each step.
346
        /// </summary>
347
        /// <param name="accountStateGetter">An <see cref="AccountStateGetter"/>
348
        /// delegate to get a previous state.
349
        /// A <c>null</c> value, which is default, means a constant function
350
        /// that returns <c>null</c>.</param>
351
        /// <param name="accountBalanceGetter">An <see cref="AccountBalanceGetter"/> delegate to
352
        /// get previous account balance.
353
        /// A <c>null</c> value, which is default, means a constant function that returns zero.
354
        /// </param>
355
        /// <returns>Enumerates pair of a transaction, and
356
        /// <see cref="ActionEvaluation"/> for each action.
357
        /// The order of pairs are the same to
358
        /// the <see cref="Transactions"/> and their
359
        /// <see cref="Transaction{T}.Actions"/> (e.g., tx&#xb9;-act&#xb9;,
360
        /// tx&#xb9;-act&#xb2;, tx&#xb2;-act&#xb9;, tx&#xb2;-act&#xb2;,
361
        /// &#x2026;).
362
        /// Note that each <see cref="IActionContext.Random"/> object has
363
        /// a unconsumed state.
364
        /// </returns>
365
        [Pure]
366
        public
367
        IEnumerable<Tuple<Transaction<T>, ActionEvaluation>> EvaluateActionsPerTx(
368
            AccountStateGetter accountStateGetter = null,
369
            AccountBalanceGetter accountBalanceGetter = null
370
        )
371 2
        {
372 2
            accountStateGetter ??= a => null;
373 2
            accountBalanceGetter ??= (a, c) => new FungibleAssetValue(c);
374

375
            IAccountStateDelta delta;
376 2
            foreach (Transaction<T> tx in Transactions)
377 2
            {
378 2
                delta = new AccountStateDeltaImpl(
379 2
                    accountStateGetter,
380 2
                    accountBalanceGetter,
381 2
                    tx.Signer
382 2
                );
383 2
                IEnumerable<ActionEvaluation> evaluations =
384 2
                    tx.EvaluateActionsGradually(
385 2
                        PreEvaluationHash,
386 2
                        Index,
387 2
                        delta,
388 2
                        Miner.Value);
389 2
                foreach (var evaluation in evaluations)
390 2
                {
391 2
                    yield return Tuple.Create(tx, evaluation);
392 2
                    delta = evaluation.OutputStates;
393 2
                }
394

395 2
                accountStateGetter = delta.GetState;
396 2
                accountBalanceGetter = delta.GetBalance;
397 2
            }
398 2
        }
399

400
        /// <summary>
401
        /// Executes every <see cref="IAction"/> in the
402
        /// <see cref="Transactions"/> and gets result states of each step of
403
        /// every <see cref="Transaction{T}"/>.
404
        /// <para>It throws an <see cref="InvalidBlockException"/> or
405
        /// an <see cref="InvalidTxException"/> if there is any
406
        /// integrity error.</para>
407
        /// <para>Otherwise it enumerates an <see cref="ActionEvaluation"/>
408
        /// for each <see cref="IAction"/>.</para>
409
        /// </summary>
410
        /// <param name="currentTime">The current time to validate
411
        /// time-wise conditions.</param>
412
        /// <param name="accountStateGetter">An <see cref="AccountStateGetter"/> delegate to get
413
        /// a previous state.  A <c>null</c> value, which is default, means a constant function
414
        /// that returns <c>null</c>.
415
        /// This affects the execution of <see cref="Transaction{T}.Actions"/>.
416
        /// </param>
417
        /// <param name="accountBalanceGetter">An <see cref="AccountBalanceGetter"/> delegate to
418
        /// get previous account balance.
419
        /// A <c>null</c> value, which is default, means a constant function that returns zero.
420
        /// This affects the execution of <see cref="Transaction{T}.Actions"/>.
421
        /// </param>
422
        /// <returns>An <see cref="ActionEvaluation"/> for each
423
        /// <see cref="IAction"/>.</returns>
424
        /// <exception cref="InvalidBlockTimestampException">Thrown when
425
        /// the <see cref="Timestamp"/> is invalid, for example, it is the far
426
        /// future than the given <paramref name="currentTime"/>.</exception>
427
        /// <exception cref="InvalidBlockIndexException">Thrown when
428
        /// the <see cref="Index"/>is invalid, for example, it is a negative
429
        /// integer.</exception>
430
        /// <exception cref="InvalidBlockDifficultyException">Thrown when
431
        /// the <see cref="Difficulty"/> is not properly configured,
432
        /// for example, it is too easy.</exception>
433
        /// <exception cref="InvalidBlockPreviousHashException">Thrown when
434
        /// <see cref="PreviousHash"/> is invalid so that
435
        /// the <see cref="Block{T}"/>s are not continuous.</exception>
436
        /// <exception cref="InvalidBlockNonceException">Thrown when
437
        /// the <see cref="Nonce"/> does not satisfy its
438
        /// <see cref="Difficulty"/> level.</exception>
439
        /// <exception cref="InvalidTxSignatureException">Thrown when its
440
        /// <see cref="Transaction{T}.Signature"/> is invalid or not signed by
441
        /// the account who corresponds to its
442
        /// <see cref="Transaction{T}.PublicKey"/>.</exception>
443
        /// <exception cref="InvalidTxPublicKeyException">Thrown when its
444
        /// <see cref="Transaction{T}.Signer"/> is not derived from its
445
        /// <see cref="Transaction{T}.PublicKey"/>.</exception>
446
        /// <exception cref="InvalidTxUpdatedAddressesException">Thrown when
447
        /// any <see cref="IAction"/> of <see cref="Transactions"/> tries
448
        /// to update the states of <see cref="Address"/>es not included
449
        /// in <see cref="Transaction{T}.UpdatedAddresses"/>.</exception>
450
        public IEnumerable<ActionEvaluation> Evaluate(
451
            DateTimeOffset currentTime,
452
            AccountStateGetter accountStateGetter = null,
453
            AccountBalanceGetter accountBalanceGetter = null
454
        )
455 2
        {
456 2
            accountStateGetter ??= a => null;
457 2
            accountBalanceGetter ??= (a, c) => new FungibleAssetValue(c);
458

459 2
            Validate(currentTime);
460 2
            Tuple<Transaction<T>, ActionEvaluation>[] txEvaluations =
461 2
                EvaluateActionsPerTx(accountStateGetter, accountBalanceGetter).ToArray();
462

463 2
            var txUpdatedAddressesPairs = txEvaluations
464 2
                    .GroupBy(tuple => tuple.Item1)
465 2
                    .Select(
466 2
                        grp => (
467 2
                            grp.Key,
468 2
                            grp.Last().Item2.OutputStates.UpdatedAddresses
469 2
                        )
470 2
                    );
471 2
            foreach (
472 2
                (Transaction<T> tx, IImmutableSet<Address> updatedAddresses)
473 2
                in txUpdatedAddressesPairs)
474 2
            {
475 2
                if (!tx.UpdatedAddresses.IsSupersetOf(updatedAddresses))
476 1
                {
477
                    const string msg =
478
                        "Actions in the transaction try to update " +
479
                        "the addresses not granted.";
480 1
                    throw new InvalidTxUpdatedAddressesException(
481 1
                        tx.Id,
482 1
                        tx.UpdatedAddresses,
483 1
                        updatedAddresses,
484 1
                        msg
485 1
                    );
486
                }
487 2
            }
488

489 2
            return txEvaluations.Select(te => te.Item2);
490 2
        }
491

492
        /// <summary>
493
        /// Gets <see cref="BlockDigest"/> representation of the <see cref="Block{T}"/>.
494
        /// </summary>
495
        /// <returns><see cref="BlockDigest"/> representation of the <see cref="Block{T}"/>.
496
        /// </returns>
497
        public BlockDigest ToBlockDigest()
498 2
        {
499 2
            return new BlockDigest(
500 2
                header: GetBlockHeader(),
501 2
                txIds: Transactions
502 2
                    .Select(tx => tx.Id.ToByteArray().ToImmutableArray())
503 2
                    .ToImmutableArray());
504 2
        }
505

506
        public override string ToString()
507 1
        {
508 1
            return Hash.ToString();
509 1
        }
510

511
        internal BlockHeader GetBlockHeader()
512 2
        {
513 2
            string timestampAsString = Timestamp.ToString(
514 2
                BlockHeader.TimestampFormat,
515 2
                CultureInfo.InvariantCulture
516 2
            );
517 2
            ImmutableArray<byte> previousHashAsArray =
518 2
                PreviousHash?.ToByteArray().ToImmutableArray() ?? ImmutableArray<byte>.Empty;
519 2
            ImmutableArray<byte> stateRootHashAsArray =
520 2
                StateRootHash?.ToByteArray().ToImmutableArray() ?? ImmutableArray<byte>.Empty;
521

522
            // FIXME: When hash is not assigned, should throw an exception.
523 2
            return new BlockHeader(
524 2
                index: Index,
525 2
                timestamp: timestampAsString,
526 2
                nonce: Nonce.ToByteArray().ToImmutableArray(),
527 2
                miner: Miner?.ToByteArray().ToImmutableArray() ?? ImmutableArray<byte>.Empty,
528 2
                difficulty: Difficulty,
529 2
                totalDifficulty: TotalDifficulty,
530 2
                previousHash: previousHashAsArray,
531 2
                txHash: TxHash?.ToByteArray().ToImmutableArray() ?? ImmutableArray<byte>.Empty,
532 2
                hash: Hash.ToByteArray().ToImmutableArray(),
533 2
                preEvaluationHash: PreEvaluationHash.ToByteArray().ToImmutableArray(),
534 2
                stateRootHash: stateRootHashAsArray
535 2
            );
536 2
        }
537

538
        internal void Validate(DateTimeOffset currentTime)
539 2
        {
540 2
            GetBlockHeader().Validate(currentTime);
541

542 2
            foreach (Transaction<T> tx in Transactions)
543 2
            {
544 2
                tx.Validate();
545 2
            }
546 2
        }
547

548
        internal RawBlock ToRawBlock()
549 1
        {
550
            // For consistency, order transactions by its id.
551 1
            return new RawBlock(
552 1
                header: GetBlockHeader(),
553 1
                transactions: Transactions.OrderBy(tx => tx.Id)
554 1
                    .Select(tx => tx.Serialize(true).ToImmutableArray()).ToImmutableArray());
555 1
        }
556

557
        private byte[] SerializeForHash(HashDigest<SHA256>? stateRootHash = null)
558 2
        {
559 2
            var dict = Bencodex.Types.Dictionary.Empty
560 2
                .Add("index", Index)
561 2
                .Add(
562 2
                    "timestamp",
563 2
                    Timestamp.ToString(BlockHeader.TimestampFormat, CultureInfo.InvariantCulture))
564 2
                .Add("difficulty", Difficulty)
565 2
                .Add("nonce", Nonce.ToByteArray());
566

567 2
            if (!(Miner is null))
568 2
            {
569 2
                dict = dict.Add("reward_beneficiary", Miner.Value.ToByteArray());
570 2
            }
571

572 2
            if (!(PreviousHash is null))
573 2
            {
574 2
                dict = dict.Add("previous_hash", PreviousHash.Value.ToByteArray());
575 2
            }
576

577 2
            if (!(TxHash is null))
578 2
            {
579 2
                dict = dict.Add("transaction_fingerprint", TxHash.Value.ToByteArray());
580 2
            }
581

582 2
            if (!(stateRootHash is null))
583 1
            {
584 1
                dict = dict.Add("state_root_hash", stateRootHash.Value.ToByteArray());
585 1
            }
586

587 2
            return new Codec().Encode(dict);
588 2
        }
589

590
        private readonly struct BlockSerializationContext
591
        {
592
            public BlockSerializationContext(bool hash, bool transactionData)
593 0
            {
594 0
                IncludeHash = hash;
595 0
                IncludeTransactionData = transactionData;
596 0
            }
597

598 0
            internal bool IncludeHash { get; }
599

600 0
            internal bool IncludeTransactionData { get; }
601
        }
602
    }
603
}

Read our documentation on viewing source code .

Loading