Example #1
0
        /// <inheritdoc />
        public bool CheckKernel(PosRuleContext context, ChainedHeader prevChainedHeader, uint headerBits,
                                long transactionTime, OutPoint prevout)
        {
            Guard.NotNull(context, nameof(context));
            Guard.NotNull(prevout, nameof(prevout));
            Guard.NotNull(prevChainedHeader, nameof(prevChainedHeader));

            var coins = this.coinView.FetchCoins(new[] { prevout.Hash });

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

            var prevBlock = this.chainIndexer.GetHeader(coins.BlockHash);

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

            var prevUtxo = coins.UnspentOutputs[0];

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

            if (IsConfirmedInNPrevBlocks(prevUtxo, prevChainedHeader, GetTargetDepthRequired(prevChainedHeader)))
            {
                this.logger.LogTrace("(-)[LOW_COIN_AGE]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }

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

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

            return(CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, prevout,
                                        (uint)transactionTime));
        }
Example #2
0
        /// <inheritdoc />
        public void CheckProofOfStake(PosRuleContext context, ChainedHeader prevChainedHeader,
                                      BlockStake prevBlockStake, Transaction transaction, uint headerBits)
        {
            Guard.NotNull(context, nameof(context));
            Guard.NotNull(prevChainedHeader, nameof(prevChainedHeader));
            Guard.NotNull(prevBlockStake, nameof(prevBlockStake));
            Guard.NotNull(transaction, nameof(transaction));

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

            var txIn = transaction.Inputs[0];

            var prevUtxo = context.UnspentOutputSet.AccessCoins(txIn.PrevOut.Hash);

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

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

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

            if (!CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, txIn.PrevOut,
                                      context.ValidationContext.ChainedHeaderToValidate.Header.Time))
            {
                this.logger.LogTrace("(-)[INVALID_STAKE_HASH_TARGET]");
                ConsensusErrors.StakeHashInvalidTarget.Throw();
            }
        }
Example #3
0
        /// <inheritdoc />
        public bool CheckStakeKernelHash(PosRuleContext context, uint headerBits, uint256 prevStakeModifier,
                                         UnspentOutputs stakingCoins, OutPoint prevout, uint transactionTime)
        {
            Guard.NotNull(context, nameof(context));
            Guard.NotNull(prevout, nameof(prevout));
            Guard.NotNull(stakingCoins, nameof(stakingCoins));

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

            // Base target.
            var 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.
            var valueIn        = stakingCoins.Outputs[prevout.N].Value.Satoshi;
            var weight         = BigInteger.ValueOf(valueIn);
            var weightedTarget = target.Multiply(weight);

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

            if (this.legacytimefield == null)
            {
                this.legacytimefield =
                    this.network.Consensus.ConsensusFactory.CreateTransaction() is IPosTransactionWithTime;
            }

            // Calculate hash.
            using (var ms = new MemoryStream())
            {
                var serializer = new BitcoinStream(ms, true);
                serializer.ReadWrite(prevStakeModifier);
                if (this.legacytimefield == true) // to stay compatible with legacy ODN
                {
                    serializer.ReadWrite(stakingCoins.Time);
                }
                serializer.ReadWrite(prevout.Hash);
                serializer.ReadWrite(prevout.N);
                serializer.ReadWrite(transactionTime);

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

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

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

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

            return(true);
        }