/// <inheritdoc />
        protected override void CheckBlockReward(ContextInformation context, Money fees, int height, Block block)
        {
            this.logger.LogTrace("({0}:{1},{2}:'{3}')", nameof(fees), fees, nameof(height), height);

            if (BlockStake.IsProofOfStake(block))
            {
                Money stakeReward     = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn;
                Money calcStakeReward = fees + this.GetProofOfStakeReward(height);

                this.logger.LogTrace("Block stake reward is {0}, calculated reward is {1}.", stakeReward, calcStakeReward);
                if (stakeReward > calcStakeReward)
                {
                    this.logger.LogTrace("(-)[BAD_COINSTAKE_AMOUNT]");
                    ConsensusErrors.BadCoinstakeAmount.Throw();
                }
            }
            else
            {
                Money blockReward = fees + this.GetProofOfWorkReward(height);
                this.logger.LogTrace("Block reward is {0}, calculated reward is {1}.", block.Transactions[0].TotalOut, blockReward);
                if (block.Transactions[0].TotalOut > blockReward)
                {
                    this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]");
                    ConsensusErrors.BadCoinbaseAmount.Throw();
                }
            }

            this.logger.LogTrace("(-)");
        }
Beispiel #2
0
        protected virtual void UpdateCoinView(ContextInformation context, Transaction tx)
        {
            ChainedBlock     index = context.BlockResult.ChainedBlock;
            UnspentOutputSet view  = context.Set;

            view.Update(tx, index.Height);
        }
Beispiel #3
0
        public override void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block)
        {
            if (BlockStake.IsProofOfStake(block))
            {
                // proof of stake invalidates previous inputs
                // and spends the inputs to new outputs with the
                // additional stake reward, next calculate the
                // reward does not exceed the consensus rules

                var stakeReward     = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn;
                var calcStakeReward = nFees + this.GetProofOfStakeReward(chainedBlock.Height);

                if (stakeReward > calcStakeReward)
                {
                    ConsensusErrors.BadCoinstakeAmount.Throw();
                }
            }
            else
            {
                var blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height);
                if (block.Transactions[0].TotalOut > blockReward)
                {
                    ConsensusErrors.BadCoinbaseAmount.Throw();
                }
            }
        }
Beispiel #4
0
        public override void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block)
        {
            this.logger.LogTrace("({0}:{1},{2}:'{3}')", nameof(nFees), nFees, nameof(chainedBlock), chainedBlock);

            if (BlockStake.IsProofOfStake(block))
            {
                // proof of stake invalidates previous inputs
                // and spends the inputs to new outputs with the
                // additional stake reward, next calculate the
                // reward does not exceed the consensus rules

                Money stakeReward     = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn;
                Money calcStakeReward = nFees + this.GetProofOfStakeReward(chainedBlock.Height);

                this.logger.LogTrace("Block stake reward is {0}, calculated reward is {1}.", stakeReward, calcStakeReward);
                if (stakeReward > calcStakeReward)
                {
                    this.logger.LogTrace("(-)[BAD_COINSTAKE_AMOUNT]");
                    ConsensusErrors.BadCoinstakeAmount.Throw();
                }
            }
            else
            {
                Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height);
                this.logger.LogTrace("Block reward is {0}, calculated reward is {1}.", block.Transactions[0].TotalOut, blockReward);
                if (block.Transactions[0].TotalOut > blockReward)
                {
                    this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]");
                    ConsensusErrors.BadCoinbaseAmount.Throw();
                }
            }

            this.logger.LogTrace("(-)");
        }
Beispiel #5
0
        public virtual void ContextualCheckBlockHeader(ContextInformation context)
        {
            Guard.NotNull(context.BestBlock, nameof(context.BestBlock));

            BlockHeader header = context.BlockResult.Block.Header;

            int nHeight = context.BestBlock.Height + 1;

            // Check proof of work
            if (header.Bits != context.NextWorkRequired)
            {
                ConsensusErrors.BadDiffBits.Throw();
            }

            // Check timestamp against prev
            if (header.BlockTime <= context.BestBlock.MedianTimePast)
            {
                ConsensusErrors.TimeTooOld.Throw();
            }

            // Check timestamp
            if (header.BlockTime > context.Time + TimeSpan.FromHours(2))
            {
                ConsensusErrors.TimeTooNew.Throw();
            }

            // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
            // check for version 2, 3 and 4 upgrades
            if ((header.Version < 2 && nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP34]) ||
                (header.Version < 3 && nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP66]) ||
                (header.Version < 4 && nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP65]))
            {
                ConsensusErrors.BadVersion.Throw();
            }
        }
Beispiel #6
0
        public override void ContextualCheckBlock(ContextInformation context)
        {
            base.ContextualCheckBlock(context);

            // TODO: fix this validation code

            //// check proof-of-stake
            //// Limited duplicity on stake: prevents block flood attack
            //// Duplicate stake allowed only when there is orphan child block
            //if (!fReindex && !fImporting && pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash))
            //	return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString(), pblock->GetProofOfStake().second, hash.ToString());

            //if (!BlockValidator.IsCanonicalBlockSignature(context.BlockResult.Block, false))
            //{
            //	//if (node != null && (int)node.Version >= CANONICAL_BLOCK_SIG_VERSION)
            //	//node.Misbehaving(100);

            //	//return false; //error("ProcessBlock(): bad block signature encoding");
            //}

            //if (!BlockValidator.IsCanonicalBlockSignature(context.BlockResult.Block, true))
            //{
            //	//if (pfrom && pfrom->nVersion >= CANONICAL_BLOCK_SIG_LOW_S_VERSION)
            //	//{
            //	//	pfrom->Misbehaving(100);
            //	//	return error("ProcessBlock(): bad block signature encoding (low-s)");
            //	//}

            //	if (!BlockValidator.EnsureLowS(context.BlockResult.Block.BlockSignatur))
            //		return false; // error("ProcessBlock(): EnsureLowS failed");
            //}
        }
        public override void CheckBlock(ContextInformation context)
        {
            this.logger.LogTrace("()");

            base.CheckBlock(context);

            Block block = context.BlockResult.Block;

            // Check timestamp.
            if (block.Header.Time > this.FutureDrift(this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp()))
            {
                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 (!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]");
        }
Beispiel #8
0
        // Stratis kernel protocol
        // coinstake must meet hash target according to the protocol:
        // kernel (input 0) must meet the formula
        //     hash(stakeModifierV2 + stakingCoins.Time + prevout.Hash + prevout.N + transactionTime) < target * weight
        // this ensures that the chance of getting a coinstake is proportional to the
        // amount of coins one owns.
        // The reason this hash is chosen is the following:
        //   stakeModifierV2: Scrambles computation to make it very difficult to precompute future proof-of-stake.
        //   stakingCoins.Time: Time of the coinstake UTXO. Slightly scrambles computation.
        //   prevout.Hash: Hash of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.
        //   prevout.N: Output number of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.
        //   transactionTime: Timestamp of the coinstake transaction.
        //   Block or transaction tx hash should not be used here as they can be generated in vast
        //   quantities so as to generate blocks faster, degrading the system back into a proof-of-work situation.
        //
        private void CheckStakeKernelHash(ContextInformation context, ChainedBlock prevChainedBlock, uint headerBits, uint prevBlockTime,
                                          BlockStake prevBlockStake, UnspentOutputs stakingCoins, OutPoint prevout, uint transactionTime)
        {
            this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4:X},{5}:{6},{7}.{8}:'{9}',{10}:'{11}/{12}',{13}:'{14}',{15}:{16})",
                                 nameof(prevChainedBlock), prevChainedBlock.HashBlock, prevChainedBlock.Height, nameof(headerBits), headerBits, nameof(prevBlockTime), prevBlockTime,
                                 nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(stakingCoins), stakingCoins.TransactionId, stakingCoins.Height,
                                 nameof(prevout), prevout, nameof(transactionTime), transactionTime);

            if (transactionTime < stakingCoins.Time)
            {
                this.logger.LogTrace("Coinstake transaction timestamp {0} is lower than its own UTXO timestamp {1}.", transactionTime, stakingCoins.Time);
                this.logger.LogTrace("(-)[BAD_STAKE_TIME]");
                ConsensusErrors.StakeTimeViolation.Throw();
            }

            // Base target.
            BigInteger target = new Target(headerBits).ToBigInteger();

            // TODO: Investigate:
            // The POS protocol should probably put a limit on the max amount that can be staked
            // not a hard limit but a limit that allow any amount to be staked with a max weight value.
            // the max weight should not exceed the max uint256 array size (array size = 32).

            // Weighted target.
            long       valueIn        = stakingCoins._Outputs[prevout.N].Value.Satoshi;
            BigInteger weight         = BigInteger.ValueOf(valueIn);
            BigInteger weightedTarget = target.Multiply(weight);

            context.Stake.TargetProofOfStake = ToUInt256(weightedTarget);
            this.logger.LogTrace("POS target is '{0}', weighted target for {1} coins is '{2}'.", ToUInt256(target), valueIn, context.Stake.TargetProofOfStake);

            uint256 stakeModifierV2 = prevBlockStake.StakeModifierV2;

            // Calculate hash.
            using (var ms = new MemoryStream())
            {
                var serializer = new BitcoinStream(ms, true);
                serializer.ReadWrite(stakeModifierV2);
                serializer.ReadWrite(stakingCoins.Time);
                serializer.ReadWrite(prevout.Hash);
                serializer.ReadWrite(prevout.N);
                serializer.ReadWrite(transactionTime);

                context.Stake.HashProofOfStake = Hashes.Hash256(ms.ToArray());
            }

            this.logger.LogTrace("Stake modifier V2 is '{0}', hash POS is '{1}'.", stakeModifierV2, context.Stake.HashProofOfStake);

            // Now check if proof-of-stake hash meets target protocol.
            BigInteger hashProofOfStakeTarget = new BigInteger(1, context.Stake.HashProofOfStake.ToBytes(false));

            if (hashProofOfStakeTarget.CompareTo(weightedTarget) > 0)
            {
                this.logger.LogTrace("(-)[TARGET_MISSED]");
                ConsensusErrors.StakeHashInvalidTarget.Throw();
            }

            this.logger.LogTrace("(-)[OK]");
        }
Beispiel #9
0
        public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block)
        {
            Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height);

            if (block.Transactions[0].TotalOut > blockReward)
            {
                ConsensusErrors.BadCoinbaseAmount.Throw();
            }
        }
Beispiel #10
0
        public virtual void CheckBlockHeader(ContextInformation context)
        {
            if (context.CheckPow && !context.BlockResult.Block.Header.CheckProofOfWork())
            {
                ConsensusErrors.HighHash.Throw();
            }

            context.NextWorkRequired = context.BlockResult.ChainedBlock.GetWorkRequired(context.Consensus);
        }
Beispiel #11
0
        public override void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler)
        {
            // compute and store the stake proofs
            this.CheckAndComputeStake(context);

            base.ExecuteBlock(context, taskScheduler);

            // TODO: a temporary fix til this methods is fixed in NStratis
            (this.stakeChain as StakeChainStore).Set(context.BlockResult.ChainedBlock, context.Stake.BlockStake);
        }
Beispiel #12
0
        protected virtual void UpdateCoinView(ContextInformation context, Transaction tx)
        {
            this.logger.LogTrace("()");

            ChainedBlock     index = context.BlockValidationContext.ChainedBlock;
            UnspentOutputSet view  = context.Set;

            view.Update(tx, index.Height);

            this.logger.LogTrace("(-)");
        }
Beispiel #13
0
        protected override void UpdateCoinView(ContextInformation context, Transaction tx)
        {
            UnspentOutputSet view = context.Set;

            if (tx.IsCoinStake)
            {
                context.Stake.TotalCoinStakeValueIn = view.GetValueIn(tx);
            }

            base.UpdateCoinView(context, tx);
        }
 private void CheckStakeKernelHash(ContextInformation context, ChainedBlock pindexPrev, uint nBits, ChainedBlock blockFrom,
                                   UnspentOutputs txPrev, BlockStake prevBlockStake, OutPoint prevout, uint nTimeTx)
 {
     if (IsProtocolV2(pindexPrev.Height + 1))
     {
         this.CheckStakeKernelHashV2(context, pindexPrev, nBits, blockFrom.Header.Time, prevBlockStake, txPrev, prevout, nTimeTx);
     }
     else
     {
         this.CheckStakeKernelHashV1();
     }
 }
Beispiel #15
0
        public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block)
        {
            this.logger.LogTrace("()");

            Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height);

            if (block.Transactions[0].TotalOut > blockReward)
            {
                this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]");
                ConsensusErrors.BadCoinbaseAmount.Throw();
            }

            this.logger.LogTrace("(-)");
        }
Beispiel #16
0
        public override void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler)
        {
            this.logger.LogTrace("()");

            // Compute and store the stake proofs.
            this.CheckAndComputeStake(context);

            base.ExecuteBlock(context, taskScheduler);

            // TODO: A temporary fix till this methods is fixed in NStratis.
            (this.stakeChain as StakeChainStore).Set(context.BlockValidationContext.ChainedBlock, context.Stake.BlockStake);

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Validates a block using the consensus rules.
        /// </summary>
        public void ValidateBlock(ContextInformation context)
        {
            this.logger.LogTrace("()");

            using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o)))
            {
                // Check that the current block has not been reorged.
                // Catching a reorg at this point will not require a rewind.
                if (context.BlockValidationContext.Block.Header.HashPrevBlock != this.Tip.HashBlock)
                {
                    this.logger.LogTrace("Reorganization detected.");
                    ConsensusErrors.InvalidPrevTip.Throw();
                }

                this.logger.LogTrace("Validating new block.");

                // Build the next block in the chain of headers. The chain header is most likely already created by
                // one of the peers so after we create a new chained block (mainly for validation)
                // we ask the chain headers for its version (also to prevent memory leaks).
                context.BlockValidationContext.ChainedBlock = new ChainedBlock(context.BlockValidationContext.Block.Header, context.BlockValidationContext.Block.Header.GetHash(), this.Tip);

                // Liberate from memory the block created above if possible.
                context.BlockValidationContext.ChainedBlock = this.Chain.GetBlock(context.BlockValidationContext.ChainedBlock.HashBlock) ?? context.BlockValidationContext.ChainedBlock;
                context.SetBestBlock(this.dateTimeProvider.GetTimeOffset());

                // == Validation flow ==

                // Check the block header is correct.
                this.Validator.CheckBlockHeader(context);
                this.Validator.ContextualCheckBlockHeader(context);

                // Calculate the consensus flags and check they are valid.
                context.Flags = this.NodeDeployments.GetFlags(context.BlockValidationContext.ChainedBlock);

                int lastCheckpointHeight = this.checkpoints.GetLastCheckpointHeight();
                if (context.BlockValidationContext.ChainedBlock.Height > lastCheckpointHeight)
                {
                    this.Validator.ContextualCheckBlock(context);

                    // Check the block itself.
                    this.Validator.CheckBlock(context);
                }
                else
                {
                    this.logger.LogTrace("Block validation partially skipped because block height {0} is not greater than last checkpointed block height {1}.", context.BlockValidationContext.ChainedBlock.Height, lastCheckpointHeight);
                }
            }

            this.logger.LogTrace("(-)[OK]");
        }
        public void CheckKernel(ContextInformation context, ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime)
        {
            var coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult();

            if (coins == null || coins.UnspentOutputs.Length != 1)
            {
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            var prevBlock = this.chain.GetBlock(coins.BlockHash);
            var prevUtxo  = coins.UnspentOutputs[0];

            //var txPrev = trasnactionStore.Get(prevout.Hash);
            //if (txPrev == null)
            //	return false;

            //// Read block header
            //var blockHashPrev = mapStore.GetBlockHash(prevout.Hash);
            //var block = blockHashPrev == null ? null : blockStore.GetBlock(blockHashPrev);
            //if (block == null)
            //	return false;

            if (IsProtocolV3((int)nTime))
            {
                if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1))
                {
                    ConsensusErrors.InvalidStakeDepth.Throw();
                }
            }
            else
            {
                var nTimeBlockFrom = prevBlock.Header.Time;
                if (nTimeBlockFrom + this.consensusOptions.StakeMinAge > nTime)
                {
                    ConsensusErrors.MinAgeViolation.Throw();
                }
            }

            var prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock);

            if (prevBlockStake == null)
            {
                ConsensusErrors.BadStakeBlock.Throw();
            }

            //if (pBlockTime)
            pBlockTime = prevBlock.Header.Time;

            this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, prevout, (uint)nTime);
        }
Beispiel #19
0
        public virtual void ContextualCheckBlockHeader(ContextInformation context)
        {
            Guard.NotNull(context.BestBlock, nameof(context.BestBlock));
            this.logger.LogTrace("()");

            BlockHeader header = context.BlockValidationContext.Block.Header;

            int nHeight = context.BestBlock.Height + 1;

            // Check proof of work.
            if (header.Bits != context.NextWorkRequired)
            {
                this.logger.LogTrace("(-)[BAD_DIFF_BITS]");
                ConsensusErrors.BadDiffBits.Throw();
            }

            // Check timestamp against prev.
            if (header.BlockTime <= context.BestBlock.MedianTimePast)
            {
                this.logger.LogTrace("(-)[TIME_TOO_OLD]");
                ConsensusErrors.TimeTooOld.Throw();
            }

            // Check timestamp.
            if (header.BlockTime > (context.Time + TimeSpan.FromHours(2)))
            {
                this.logger.LogTrace("(-)[TIME_TOO_NEW]");
                ConsensusErrors.TimeTooNew.Throw();
            }

            // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
            // check for version 2, 3 and 4 upgrades.
            if (((header.Version < 2) && (nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP34])) ||
                ((header.Version < 3) && (nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP66])) ||
                ((header.Version < 4) && (nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP65])))
            {
                this.logger.LogTrace("(-)[BAD_VERSION]");
                ConsensusErrors.BadVersion.Throw();
            }

            // Check that the block header hash matches the known checkpointed value, if any.
            if (!this.Checkpoints.CheckHardened(nHeight, header.GetHash()))
            {
                this.logger.LogTrace("(-)[CHECKPOINT_VIOLATION]");
                ConsensusErrors.CheckpointViolation.Throw();
            }

            this.logger.LogTrace("(-)[OK]");
        }
        /// <inheritdoc />
        protected override void UpdateCoinView(ContextInformation context, Transaction transaction)
        {
            this.logger.LogTrace("()");

            UnspentOutputSet view = context.Set;

            if (transaction.IsCoinStake)
            {
                context.Stake.TotalCoinStakeValueIn = view.GetValueIn(transaction);
            }

            base.UpdateCoinView(context, transaction);

            this.logger.LogTrace("(-)");
        }
Beispiel #21
0
        public override void CheckBlockHeader(ContextInformation context)
        {
            context.SetStake();

            if (context.Stake.BlockStake.IsProofOfWork())
            {
                if (context.CheckPow && !context.BlockResult.Block.Header.CheckProofOfWork())
                {
                    ConsensusErrors.HighHash.Throw();
                }
            }

            context.NextWorkRequired = StakeValidator.GetNextTargetRequired(this.stakeChain, context.BlockResult.ChainedBlock.Previous, context.Consensus,
                                                                            context.Stake.BlockStake.IsProofOfStake());
        }
Beispiel #22
0
        public override void ContextualCheckBlockHeader(ContextInformation context)
        {
            base.ContextualCheckBlockHeader(context);

            var chainedBlock = context.BlockResult.ChainedBlock;

            if (!StakeValidator.IsProtocolV3((int)chainedBlock.Header.Time))
            {
                if (chainedBlock.Header.Version > BlockHeader.CURRENT_VERSION)
                {
                    ConsensusErrors.BadVersion.Throw();
                }
            }

            if (StakeValidator.IsProtocolV2(chainedBlock.Height) && chainedBlock.Header.Version < 7)
            {
                ConsensusErrors.BadVersion.Throw();
            }
            else if (!StakeValidator.IsProtocolV2(chainedBlock.Height) && chainedBlock.Header.Version > 6)
            {
                ConsensusErrors.BadVersion.Throw();
            }

            if (context.Stake.BlockStake.IsProofOfWork() && chainedBlock.Height > this.ConsensusParams.LastPOWBlock)
            {
                ConsensusErrors.ProofOfWorkTooHeigh.Throw();
            }

            // Check coinbase timestamp
            if (chainedBlock.Header.Time > FutureDrift(context.BlockResult.Block.Transactions[0].Time, chainedBlock.Height))
            {
                ConsensusErrors.TimeTooNew.Throw();
            }

            // Check coinstake timestamp
            if (context.Stake.BlockStake.IsProofOfStake() &&
                !PosConsensusValidator.CheckCoinStakeTimestamp(chainedBlock.Height, chainedBlock.Header.Time, context.BlockResult.Block.Transactions[1].Time))
            {
                ConsensusErrors.StakeTimeViolation.Throw();
            }

            // Check timestamp against prev
            if (chainedBlock.Header.Time <= StakeValidator.GetPastTimeLimit(chainedBlock.Previous) ||
                FutureDrift(chainedBlock.Header.Time, chainedBlock.Height) < chainedBlock.Previous.Header.Time)
            {
                ConsensusErrors.BlockTimestampTooEarly.Throw();
            }
        }
Beispiel #23
0
        public override void CheckBlock(ContextInformation context)
        {
            base.CheckBlock(context);

            var block = context.BlockResult.Block;

            // Check timestamp
            if (block.Header.Time > FutureDriftV2(DateTime.UtcNow.Ticks))
            {
                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)
                {
                    ConsensusErrors.BadStakeBlock.Throw();
                }

                // Second transaction must be coinstake, the rest must not be
                if (!block.Transactions[1].IsCoinStake)
                {
                    ConsensusErrors.BadStakeBlock.Throw();
                }

                if (block.Transactions.Skip(2).Any(t => t.IsCoinStake))
                {
                    ConsensusErrors.BadMultipleCoinstake.Throw();
                }
            }

            // Check proof-of-stake block signature
            if (!CheckBlockSignature(block))
            {
                ConsensusErrors.BadBlockSignature.Throw();
            }

            // Check transactions
            foreach (var transaction in block.Transactions)
            {
                // check transaction timestamp
                if (block.Header.Time < transaction.Time)
                {
                    ConsensusErrors.BlockTimeBeforeTrx.Throw();
                }
            }
        }
        public void CheckProofOfStake(ContextInformation context, ChainedBlock pindexPrev, BlockStake prevBlockStake,
                                      Transaction tx, uint nBits)
        {
            if (!tx.IsCoinStake)
            {
                ConsensusErrors.NonCoinstake.Throw();
            }

            // Kernel (input 0) must match the stake hash target per coin age (nBits)
            var txIn = tx.Inputs[0];

            // First try finding the previous transaction in database
            var coins = this.coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult();

            if (coins == null || coins.UnspentOutputs.Length != 1)
            {
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            var prevBlock = this.chain.GetBlock(coins.BlockHash);
            var prevUtxo  = coins.UnspentOutputs[0];

            // Verify signature
            if (!this.VerifySignature(prevUtxo, tx, 0, ScriptVerify.None))
            {
                ConsensusErrors.CoinstakeVerifySignatureFailed.Throw();
            }

            // Min age requirement
            if (IsProtocolV3((int)tx.Time))
            {
                if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1))
                {
                    ConsensusErrors.InvalidStakeDepth.Throw();
                }
            }
            else
            {
                var nTimeBlockFrom = prevBlock.Header.Time;
                if (nTimeBlockFrom + this.consensusOptions.StakeMinAge > tx.Time)
                {
                    ConsensusErrors.MinAgeViolation.Throw();
                }
            }

            this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, txIn.PrevOut, tx.Time);
        }
Beispiel #25
0
        public override void ContextualCheckBlockHeader(ContextInformation context)
        {
            this.logger.LogTrace("()");
            base.ContextualCheckBlockHeader(context);

            ChainedBlock chainedBlock = context.BlockValidationContext.ChainedBlock;

            this.logger.LogTrace("Height of block is {0}, block timestamp is {1}, previous block timestamp is {2}, block version is 0x{3:x}.", chainedBlock.Height, chainedBlock.Header.Time, chainedBlock.Previous.Header.Time, chainedBlock.Header.Version);

            if (chainedBlock.Header.Version < 7)
            {
                this.logger.LogTrace("(-)[BAD_VERSION]");
                ConsensusErrors.BadVersion.Throw();
            }

            if (context.Stake.BlockStake.IsProofOfWork() && (chainedBlock.Height > this.ConsensusParams.LastPOWBlock))
            {
                this.logger.LogTrace("(-)[POW_TOO_HIGH]");
                ConsensusErrors.ProofOfWorkTooHeigh.Throw();
            }

            // Check coinbase timestamp.
            if (chainedBlock.Header.Time > this.FutureDrift(context.BlockValidationContext.Block.Transactions[0].Time))
            {
                this.logger.LogTrace("(-)[TIME_TOO_NEW]");
                ConsensusErrors.TimeTooNew.Throw();
            }

            // Check coinstake timestamp.
            if (context.Stake.BlockStake.IsProofOfStake() &&
                !this.CheckCoinStakeTimestamp(chainedBlock.Height, chainedBlock.Header.Time, context.BlockValidationContext.Block.Transactions[1].Time))
            {
                this.logger.LogTrace("(-)[BAD_TIME]");
                ConsensusErrors.StakeTimeViolation.Throw();
            }

            // Check timestamp against prev.
            if (chainedBlock.Header.Time <= chainedBlock.Previous.Header.Time)
            {
                this.logger.LogTrace("(-)[TIME_TOO_EARLY]");
                ConsensusErrors.BlockTimestampTooEarly.Throw();
            }

            this.logger.LogTrace("(-)[OK]");
        }
        public void CheckKernel(ContextInformation context, ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:0x{3:X},{4}:{5},{6}:'{7}.{8}')", nameof(pindexPrev), pindexPrev,
                                 nameof(nBits), nBits, nameof(nTime), nTime, nameof(prevout), prevout.Hash, prevout.N);

            // TODO: https://github.com/stratisproject/StratisBitcoinFullNode/issues/397
            FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult();

            if ((coins == null) || (coins.UnspentOutputs.Length != 1))
            {
                this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            ChainedBlock prevBlock = this.chain.GetBlock(coins.BlockHash);

            if (prevBlock == null)
            {
                this.logger.LogTrace("(-)[REORG]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            UnspentOutputs prevUtxo = coins.UnspentOutputs[0];

            if (this.IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1))
            {
                this.logger.LogTrace("(-)[LOW_COIN_AGE]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }

            BlockStake prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock);

            if (prevBlockStake == null)
            {
                this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]");
                ConsensusErrors.BadStakeBlock.Throw();
            }

            pBlockTime = prevBlock.Header.Time;

            this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, prevout, (uint)nTime);

            this.logger.LogTrace("(-):{0}={1}", nameof(pBlockTime), pBlockTime);
        }
Beispiel #27
0
        public override void CheckBlockHeader(ContextInformation context)
        {
            this.logger.LogTrace("()");
            context.SetStake();

            if (context.Stake.BlockStake.IsProofOfWork())
            {
                if (context.CheckPow && !context.BlockValidationContext.Block.Header.CheckProofOfWork())
                {
                    this.logger.LogTrace("(-)[HIGH_HASH]");
                    ConsensusErrors.HighHash.Throw();
                }
            }

            context.NextWorkRequired = StakeValidator.GetNextTargetRequired(this.stakeChain, context.BlockValidationContext.ChainedBlock.Previous, context.Consensus,
                                                                            context.Stake.BlockStake.IsProofOfStake());

            this.logger.LogTrace("(-)[OK]");
        }
Beispiel #28
0
        public void CheckAndComputeStake(ContextInformation context)
        {
            var pindex     = context.BlockResult.ChainedBlock;
            var block      = context.BlockResult.Block;
            var blockStake = context.Stake.BlockStake;

            // Verify hash target and signature of coinstake tx
            if (BlockStake.IsProofOfStake(block))
            {
                var pindexPrev = pindex.Previous;

                var prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock);
                if (prevBlockStake == null)
                {
                    ConsensusErrors.PrevStakeNull.Throw();
                }

                this.stakeValidator.CheckProofOfStake(context, pindexPrev, prevBlockStake, block.Transactions[1], pindex.Header.Bits.ToCompact());
            }

            // PoW is checked in CheckBlock()
            if (BlockStake.IsProofOfWork(block))
            {
                context.Stake.HashProofOfStake = pindex.Header.GetPoWHash();
            }

            // TODO: is this the same as chain work?
            // compute chain trust score
            //pindexNew.nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();

            // compute stake entropy bit for stake modifier
            if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit()))
            {
                ConsensusErrors.SetStakeEntropyBitFailed.Throw();
            }

            // Record proof hash value
            blockStake.HashProof = context.Stake.HashProofOfStake;

            // compute stake modifier
            this.stakeValidator.ComputeStakeModifier(this.chain, pindex, blockStake);
        }
Beispiel #29
0
        public void CheckProofOfStake(ContextInformation context, ChainedBlock pindexPrev, BlockStake prevBlockStake, Transaction tx, uint headerBits)
        {
            this.logger.LogTrace("({0}:'{1}',{2}.{3}:'{4}',{5}:0x{6:X})", nameof(pindexPrev), pindexPrev.HashBlock, nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(headerBits), headerBits);

            if (!tx.IsCoinStake)
            {
                this.logger.LogTrace("(-)[NO_COINSTAKE]");
                ConsensusErrors.NonCoinstake.Throw();
            }

            // Kernel (input 0) must match the stake hash target per coin age (nBits).
            TxIn txIn = tx.Inputs[0];

            // First try finding the previous transaction in database.
            FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult();

            if ((coins == null) || (coins.UnspentOutputs.Length != 1))
            {
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            ChainedBlock   prevBlock = this.chain.GetBlock(coins.BlockHash);
            UnspentOutputs prevUtxo  = coins.UnspentOutputs[0];

            // Verify signature.
            if (!this.VerifySignature(prevUtxo, tx, 0, ScriptVerify.None))
            {
                this.logger.LogTrace("(-)[BAD_SIGNATURE]");
                ConsensusErrors.CoinstakeVerifySignatureFailed.Throw();
            }

            // Min age requirement.
            if (this.IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1))
            {
                this.logger.LogTrace("(-)[BAD_STAKE_DEPTH]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }

            this.CheckStakeKernelHash(context, pindexPrev, headerBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, txIn.PrevOut, tx.Time);

            this.logger.LogTrace("(-)[OK]");
        }
        /// <summary>
        /// Validates a block using the consensus rules and executes it (processes it and adds it as a tip to consensus).
        /// </summary>
        /// <param name="context">A context that contains all information required to validate the block.</param>
        internal async Task ValidateAndExecuteBlockAsync(ContextInformation context)
        {
            this.logger.LogTrace("()");

            this.ValidateBlock(context);

            // Load the UTXO set of the current block. UTXO may be loaded from cache or from disk.
            // The UTXO set is stored in the context.
            this.logger.LogTrace("Loading UTXO set of the new block.");
            context.Set = new UnspentOutputSet();
            using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddUTXOFetchingTime(o)))
            {
                uint256[]          ids   = this.GetIdsToFetch(context.BlockValidationContext.Block, context.Flags.EnforceBIP30);
                FetchCoinsResponse coins = await this.UTXOSet.FetchCoinsAsync(ids).ConfigureAwait(false);

                context.Set.SetCoins(coins.UnspentOutputs);
            }

            // Attempt to load into the cache the next set of UTXO to be validated.
            // The task is not awaited so will not stall main validation process.
            this.TryPrefetchAsync(context.Flags);

            // Validate the UTXO set is correctly spent.
            this.logger.LogTrace("Executing block.");
            using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o)))
            {
                this.Validator.ExecuteBlock(context, null);
            }

            // Persist the changes to the coinview. This will likely only be stored in memory,
            // unless the coinview treashold is reached.
            this.logger.LogTrace("Saving coinview changes.");
            await this.UTXOSet.SaveChangesAsync(context.Set.GetCoins(this.UTXOSet), null, this.Tip.HashBlock, context.BlockValidationContext.ChainedBlock.HashBlock).ConfigureAwait(false);

            // Set the new tip.
            this.Tip = context.BlockValidationContext.ChainedBlock;
            this.logger.LogTrace("(-)[OK]");
        }