Exemplo n.º 1
0
        // Taking the selected transaction receipts from consensus, CreateHeader removes bad
        // transactions, adds necessary system transactions, emulate the transactions, calculate
        // stateHash and finally returns a blockHeader
        public BlockHeader?CreateHeader(
            ulong index, IReadOnlyCollection <TransactionReceipt> receipts, ulong nonce, out TransactionReceipt[] receiptsTaken
            )
        {
            Logger.LogTrace("CreateHeader");
            if (_blockManager.GetHeight() >= index)
            {
                Logger.LogWarning("Block already produced");
                receiptsTaken = new TransactionReceipt[] {};
                return(null);
            }

            // we don't need to verify receipts here
            // verfification will be done during emulation

            // But we need to verify the hash as we map the receipts with its hash
            // we skip the transactions with hash mismatch
            receipts = receipts.Where(receipt =>
                                      receipt.Transaction.FullHash(receipt.Signature, HardforkHeights.IsHardfork_9Active(index)).Equals(receipt.Hash)).ToList();

            receipts = receipts.OrderBy(receipt => receipt, new ReceiptComparer())
                       .ToList();

            var cycle        = index / StakingContract.CycleDuration;
            var indexInCycle = index % StakingContract.CycleDuration;

            // try to add necessary system transactions at the end
            if (cycle > 0 && indexInCycle == StakingContract.AttendanceDetectionDuration)
            {
                var txToAdd = DistributeCycleRewardsAndPenaltiesTxReceipt(index);
                if (receipts.Select(x => x.Hash).Contains(txToAdd.Hash))
                {
                    Logger.LogDebug("DistributeCycleRewardsAndPenaltiesTxReceipt is already in txPool");
                }
                else
                {
                    receipts = receipts.Concat(new[] { txToAdd }).ToList();
                }
            }
            else if (indexInCycle == StakingContract.VrfSubmissionPhaseDuration)
            {
                var txToAdd = FinishVrfLotteryTxReceipt(index);
                if (receipts.Select(x => x.Hash).Contains(txToAdd.Hash))
                {
                    Logger.LogDebug("FinishVrfLotteryTxReceipt is already in txPool");
                }
                else
                {
                    receipts = receipts.Concat(new[] { txToAdd }).ToList();
                }
            }
            else if (cycle > 0 && indexInCycle == 0)
            {
                var txToAdd = FinishCycleTxReceipt(index);
                if (receipts.Select(x => x.Hash).Contains(txToAdd.Hash))
                {
                    Logger.LogDebug("FinishCycleTxReceipt is already in txPool");
                }
                else
                {
                    receipts = receipts.Concat(new[] { txToAdd }).ToList();
                }
            }

            if (_blockManager.LatestBlock().Header.Index + 1 != index)
            {
                throw new InvalidOperationException(
                          $"Latest block is {_blockManager.LatestBlock().Header.Index} " +
                          $"with hash {_blockManager.LatestBlock().Hash.ToHex()}, " +
                          $"but we are trying to create block {index}");
            }

            var blockWithTransactions =
                new BlockBuilder(_blockManager.LatestBlock().Header)
                .WithTransactions(receipts)
                .Build(nonce);

            var(operatingError, removedTxs, stateHash, returnedTxs) =
                _blockManager.Emulate(blockWithTransactions.Block, blockWithTransactions.Transactions);


            var badReceipts = new HashSet <TransactionReceipt>(removedTxs.Concat(returnedTxs));

            receiptsTaken = receipts.Where(receipt => !badReceipts.Contains(receipt)).ToArray();

            blockWithTransactions = new BlockBuilder(_blockManager.LatestBlock().Header)
                                    .WithTransactions(receiptsTaken)
                                    .Build(nonce);

            if (operatingError != OperatingError.Ok)
            {
                throw new InvalidOperationException($"Cannot assemble block: error {operatingError}");
            }

            return(new BlockHeader
            {
                Index = blockWithTransactions.Block.Header.Index,
                MerkleRoot = blockWithTransactions.Block.Header.MerkleRoot,
                Nonce = nonce,
                PrevBlockHash = blockWithTransactions.Block.Header.PrevBlockHash,
                StateHash = stateHash
            });
        }