/// <summary> /// Checks and computes stake. /// </summary> /// <param name="context">Context that contains variety of information regarding blocks validation and execution.</param> /// <exception cref="ConsensusErrors.PrevStakeNull">Thrown if previous stake is not found.</exception> /// <exception cref="ConsensusErrors.SetStakeEntropyBitFailed">Thrown if failed to set stake entropy bit.</exception> private void CheckAndComputeStake(RuleContext context) { this.logger.LogTrace("()"); ChainedBlock chainedBlock = context.BlockValidationContext.ChainedBlock; Block block = context.BlockValidationContext.Block; BlockStake blockStake = context.Stake.BlockStake; // Verify hash target and signature of coinstake tx. if (BlockStake.IsProofOfStake(block)) { ChainedBlock prevChainedBlock = chainedBlock.Previous; BlockStake prevBlockStake = this.stakeChain.Get(prevChainedBlock.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } // Only do proof of stake validation for blocks that are after the assumevalid block or after the last checkpoint. if (!context.SkipValidation) { this.StakeValidator.CheckProofOfStake(context.Stake, prevChainedBlock, prevBlockStake, block.Transactions[1], chainedBlock.Header.Bits.ToCompact()); } else { this.logger.LogTrace("POS validation skipped for block at height {0}.", chainedBlock.Height); } } // PoW is checked in CheckBlock(). if (BlockStake.IsProofOfWork(block)) { context.Stake.HashProofOfStake = chainedBlock.Header.GetPoWHash(); } // Compute stake entropy bit for stake modifier. if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { this.logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]"); ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value. blockStake.HashProof = context.Stake.HashProofOfStake; int lastCheckpointHeight = this.Checkpoints.GetLastCheckpointHeight(); if (chainedBlock.Height > lastCheckpointHeight) { // Compute stake modifier. ChainedBlock prevChainedBlock = chainedBlock.Previous; BlockStake blockStakePrev = prevChainedBlock == null ? null : this.stakeChain.Get(prevChainedBlock.HashBlock); blockStake.StakeModifierV2 = this.StakeValidator.ComputeStakeModifierV2(prevChainedBlock, blockStakePrev, blockStake.IsProofOfWork() ? chainedBlock.HashBlock : blockStake.PrevoutStake.Hash); } else if (chainedBlock.Height == lastCheckpointHeight) { // Copy checkpointed stake modifier. CheckpointInfo checkpoint = this.Checkpoints.GetCheckpoint(lastCheckpointHeight); blockStake.StakeModifierV2 = checkpoint.StakeModifierV2; this.logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2); } else { this.logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedBlock.Height, lastCheckpointHeight); } this.logger.LogTrace("(-)[OK]"); }
/// <inheritdoc /> public override void CheckBlock(RuleContext context) { this.logger.LogTrace("()"); base.CheckBlock(context); Block block = context.BlockValidationContext.Block; // Check timestamp. if (block.Header.Time > this.FutureDrift(this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp())) { // The block can be valid only after its time minus the future drift. context.BlockValidationContext.RejectUntil = Utils.UnixTimeToDateTime(block.Header.Time - this.FutureDrift(0)).UtcDateTime; this.logger.LogTrace("(-)[TIME_TOO_FAR]"); ConsensusErrors.BlockTimestampTooFar.Throw(); } if (BlockStake.IsProofOfStake(block)) { // Coinbase output should be empty if proof-of-stake block. if ((block.Transactions[0].Outputs.Count != 1) || !block.Transactions[0].Outputs[0].IsEmpty) { this.logger.LogTrace("(-)[COINBASE_NOT_EMPTY]"); ConsensusErrors.BadStakeBlock.Throw(); } // Second transaction must be coinstake, the rest must not be. if (!block.Transactions[1].IsCoinStake) { this.logger.LogTrace("(-)[NO_COINSTAKE]"); ConsensusErrors.BadStakeBlock.Throw(); } if (block.Transactions.Skip(2).Any(t => t.IsCoinStake)) { this.logger.LogTrace("(-)[MULTIPLE_COINSTAKE]"); ConsensusErrors.BadMultipleCoinstake.Throw(); } } // Check proof-of-stake block signature. if (!this.CheckBlockSignature(block)) { this.logger.LogTrace("(-)[BAD_SIGNATURE]"); ConsensusErrors.BadBlockSignature.Throw(); } // Check transactions. foreach (Transaction transaction in block.Transactions) { // Check transaction timestamp. if (block.Header.Time < transaction.Time) { this.logger.LogTrace("Block contains transaction with timestamp {0}, which is greater than block's timestamp {1}.", transaction.Time, block.Header.Time); this.logger.LogTrace("(-)[TX_TIME_MISMATCH]"); ConsensusErrors.BlockTimeBeforeTrx.Throw(); } } this.logger.LogTrace("(-)[OK]"); }