// 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 }); }